StackStateAnimator.java revision eb973565f3efc6417ca35363e4d6c642947775d8
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; 22572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.animation.ValueAnimator; 23572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.View; 24572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.AnimationUtils; 25572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport android.view.animation.Interpolator; 26eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 27eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport com.android.systemui.R; 28572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport com.android.systemui.statusbar.ExpandableView; 29572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 30572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekimport java.util.ArrayList; 31eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.HashSet; 32eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Set; 33eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinekimport java.util.Stack; 34572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 35572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek/** 36572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek * An stack state animator which handles animations to new StackScrollStates 37572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek */ 38572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinekpublic class StackStateAnimator { 39572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 40572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek private static final int ANIMATION_DURATION = 360; 41eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag; 42eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag; 43eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag; 44eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_HEIGHT = R.id.height_animator_tag; 45eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_ANIMATOR_TOP_INSET = R.id.top_inset_animator_tag; 46eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag; 47eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag; 48eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag; 49eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_HEIGHT = R.id.height_animator_end_value_tag; 50eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private static final int TAG_END_TOP_INSET = R.id.top_inset_animator_end_value_tag; 51572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 52572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek private final Interpolator mFastOutSlowInInterpolator; 53572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public NotificationStackScrollLayout mHostLayout; 540dd6881ea481c855976214807c17595b34a2920aJorim Jaggi private ArrayList<NotificationStackScrollLayout.AnimationEvent> mHandledEvents = 55572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek new ArrayList<>(); 56eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private ArrayList<NotificationStackScrollLayout.AnimationEvent> mNewEvents = 57eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek new ArrayList<>(); 58eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private Set<Animator> mAnimatorSet = new HashSet<Animator>(); 59eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private Stack<AnimatorListenerAdapter> mAnimationListenerPool 60eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek = new Stack<AnimatorListenerAdapter>(); 61572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 62572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public StackStateAnimator(NotificationStackScrollLayout hostLayout) { 63572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHostLayout = hostLayout; 64572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(hostLayout.getContext(), 65eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek android.R.interpolator.fast_out_slow_in); 66572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 67572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 68572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public boolean isRunning() { 69eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return !mAnimatorSet.isEmpty(); 70572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 71572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 72572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void startAnimationForEvents( 730dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> mAnimationEvents, 74572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 75eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 76eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek processAnimationEvents(mAnimationEvents, finalState); 77eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 78eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek boolean hasNewEvents = !mNewEvents.isEmpty(); 79572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek int childCount = mHostLayout.getChildCount(); 80572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek for (int i = 0; i < childCount; i++) { 81572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek final ExpandableView child = (ExpandableView) mHostLayout.getChildAt(i); 82572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState.ViewState viewState = finalState.getViewStateForView(child); 83572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek if (viewState == null) { 84572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek continue; 85572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 86572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 87eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek startAnimations(child, viewState, hasNewEvents); 88572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 89572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek child.setClipBounds(null); 90572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 91eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!isRunning()) { 92eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no child has preformed any animation, lets finish 93eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek onAnimationFinished(); 94eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 95572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 96572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 97eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 98eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Start an animation to the given viewState 99eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 100eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startAnimations(final ExpandableView child, StackScrollState.ViewState viewState, 101eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek boolean hasNewEvents) { 102eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek int childVisibility = child.getVisibility(); 103eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek boolean wasVisible = childVisibility == View.VISIBLE; 104eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final float alpha = viewState.alpha; 105eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!wasVisible && alpha != 0 && !viewState.gone) { 106eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.VISIBLE); 107eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 108eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationY animation 109eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (child.getTranslationY() != viewState.yTranslation) { 110eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek startYTranslationAnimation(child, viewState, hasNewEvents); 111eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 112eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start translationZ animation 113eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (child.getTranslationZ() != viewState.zTranslation) { 114eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek startZTranslationAnimation(child, viewState, hasNewEvents); 115eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 116eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start alpha animation 11759b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi if (alpha != child.getAlpha()) { 118eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek startAlphaAnimation(child, viewState, hasNewEvents); 119eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 120eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // start height animation 121eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (viewState.height != child.getActualHeight()) { 122eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek startHeightAnimation(child, viewState, hasNewEvents); 12359b5a356b828fe60ea2874b0680a1bf7c84809a1Jorim Jaggi } 124572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 125572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 126eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startHeightAnimation(final ExpandableView child, 127eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek StackScrollState.ViewState viewState, boolean hasNewEvents) { 128eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Integer previousEndValue = getChildTag(child,TAG_END_HEIGHT); 129eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousEndValue != null && previousEndValue == viewState.height) { 130eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 131eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 132eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ValueAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_HEIGHT); 133eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); 134eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (newDuration <= 0) { 135eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator == null) { 136eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no animation was running, but also no new animation should be performed, 137eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // lets just apply the value 138eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setActualHeight(viewState.height); 139eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 140eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 141eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 142eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 143eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ValueAnimator animator = ValueAnimator.ofInt(child.getActualHeight(), viewState.height); 144eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 145572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek @Override 146572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek public void onAnimationUpdate(ValueAnimator animation) { 147572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek child.setActualHeight((int) animation.getAnimatedValue()); 148572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 149572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek }); 150eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 151eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setDuration(newDuration); 152eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 153eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 154eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 155eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 156eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 157eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, null); 158eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_HEIGHT, null); 159eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 160eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 161eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.start(); 162eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_HEIGHT, animator); 163eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_HEIGHT, viewState.height); 164eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 165eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 166eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startAlphaAnimation(final ExpandableView child, 167eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final StackScrollState.ViewState viewState, boolean hasNewEvents) { 168eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final float endAlpha = viewState.alpha; 169eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_ALPHA); 170eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousEndValue != null && previousEndValue == endAlpha) { 171eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 172eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 173eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA); 174eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); 175eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (newDuration <= 0) { 176eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator == null) { 177eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no animation was running, but also no new animation should be performed, 178eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // lets just apply the value 179eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setAlpha(endAlpha); 180eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (endAlpha == 0) { 181eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.INVISIBLE); 182eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 183eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 184eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 185eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 186eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 187eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA, 188eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.getAlpha(), endAlpha); 189eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 190eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // Handle layer type 191eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final int currentLayerType = child.getLayerType(); 192eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(View.LAYER_TYPE_HARDWARE, null); 193eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 194eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public boolean mWasCancelled; 195eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 196eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 197eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 198eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setLayerType(currentLayerType, null); 199eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (endAlpha == 0 && !mWasCancelled) { 200eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setVisibility(View.INVISIBLE); 201eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 202eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, null); 203eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_ALPHA, null); 204eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 205eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 206eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 207eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationCancel(Animator animation) { 208eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = true; 209eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 210eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 211eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 212eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationStart(Animator animation) { 213eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = false; 214eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 215eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 216eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 217eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 218eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 219eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 220eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 221eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 222eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 223eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 224eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.start(); 225eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_ALPHA, animator); 226eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_ALPHA, endAlpha); 227572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 228572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek 22939610545f0c2714a3526bc935effe57b421542d1Selim Cinek /** 230eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @return an adapter which ensures that onAnimationFinished is called once no animation is 231eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * running anymore 232eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 233eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private AnimatorListenerAdapter getGlobalAnimationFinishedListener() { 234eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!mAnimationListenerPool.empty()) { 235eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return mAnimationListenerPool.pop(); 236eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 237eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 238eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // We need to create a new one, no reusable ones found 239eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return new AnimatorListenerAdapter() { 240eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private boolean mWasCancelled; 241eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 242eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 243eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 244eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mAnimatorSet.remove(animation); 245eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (mAnimatorSet.isEmpty() && !mWasCancelled) { 246eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek onAnimationFinished(); 247eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 248eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mAnimationListenerPool.push(this); 249eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 250eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 251eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 252eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationCancel(Animator animation) { 253eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = true; 254eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 255eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 256eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 257eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationStart(Animator animation) { 258eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mAnimatorSet.add(animation); 259eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mWasCancelled = false; 260eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 261eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }; 262eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 263eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 264eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 265eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startZTranslationAnimation(final ExpandableView child, 266eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek final StackScrollState.ViewState viewState, boolean hasNewEvents) { 267eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z); 268eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousEndValue != null && previousEndValue == viewState.zTranslation) { 269eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 270eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 271eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z); 272eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); 273eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (newDuration <= 0) { 274eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator == null) { 275eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no animation was running, but also no new animation should be performed, 276eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // lets just apply the value 277eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTranslationZ(viewState.zTranslation); 278eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 279eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 280eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 281eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 282eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z, 283eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.getTranslationZ(), viewState.zTranslation); 284eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 285eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 286eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 287eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 288eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 289eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 290eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); 291eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Z, null); 292eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 293eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 294eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.start(); 295eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); 296eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Z, viewState.zTranslation); 297eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 298eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 299eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void startYTranslationAnimation(final ExpandableView child, 300eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek StackScrollState.ViewState viewState, boolean hasNewEvents) { 301eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); 302eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousEndValue != null && previousEndValue == viewState.yTranslation) { 303eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 304eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 305eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); 306eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek long newDuration = cancelAnimatorAndGetNewDuration(previousAnimator, hasNewEvents); 307eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (newDuration <= 0) { 308eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator == null) { 309eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // no animation was running, but also no new animation should be performed, 310eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // lets just apply the value 311eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTranslationY(viewState.yTranslation); 312eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 313eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return; 314eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 315eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 316eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 317eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.getTranslationY(), viewState.yTranslation); 318eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.setInterpolator(mFastOutSlowInInterpolator); 319eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(getGlobalAnimationFinishedListener()); 320eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // remove the tag when the animation is finished 321eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.addListener(new AnimatorListenerAdapter() { 322eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek @Override 323eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek public void onAnimationEnd(Animator animation) { 324eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); 325eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Y, null); 326eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 327eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek }); 328eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek animator.start(); 329eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); 330eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek child.setTag(TAG_END_TRANSLATION_Y, viewState.yTranslation); 331eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 332eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 333eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private <T> T getChildTag(View child, int tag) { 334eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return (T) child.getTag(tag); 335eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 336eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 337eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 338eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Cancel the previous animator and get the duration of the new animation. 33939610545f0c2714a3526bc935effe57b421542d1Selim Cinek * 340eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param previousAnimator the animator which was running before 341eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param hasNewEvents indicating whether new events came in in this animation 342eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @return the new duration 343eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek */ 344eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private long cancelAnimatorAndGetNewDuration(ValueAnimator previousAnimator, 345eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek boolean hasNewEvents) { 346eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (previousAnimator != null) { 347eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek previousAnimator.cancel(); 348eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!hasNewEvents) { 349eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // This is only an update, no new event came in. lets just take the remaining 350eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // duration as the new duration 351eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return (long) ((1.0f - previousAnimator.getAnimatedFraction()) * 352eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek previousAnimator.getDuration()); 353eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 354eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } else if (!hasNewEvents){ 355eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return 0; 356eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 357eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek return ANIMATION_DURATION; 358eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 359eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 360eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void onAnimationFinished() { 361eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHandledEvents.clear(); 362eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mNewEvents.clear(); 363eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mHostLayout.onChildAnimationFinished(); 364eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 365eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 366eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek /** 367eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * Process the animationEvents for a new animation 368eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * 369eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek * @param animationEvents the animation events for the animation to perform 37039610545f0c2714a3526bc935effe57b421542d1Selim Cinek * @param finalState the final state to animate to 37139610545f0c2714a3526bc935effe57b421542d1Selim Cinek */ 372eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek private void processAnimationEvents( 3730dd6881ea481c855976214807c17595b34a2920aJorim Jaggi ArrayList<NotificationStackScrollLayout.AnimationEvent> animationEvents, 374572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek StackScrollState finalState) { 375eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mNewEvents.clear(); 3760dd6881ea481c855976214807c17595b34a2920aJorim Jaggi for (NotificationStackScrollLayout.AnimationEvent event: animationEvents) { 377572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek View changingView = event.changingView; 378eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (!mHandledEvents.contains(event)) { 379eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (event.animationType == NotificationStackScrollLayout.AnimationEvent 380eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek .ANIMATION_TYPE_ADD) { 381eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek 382eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // This item is added, initialize it's properties. 383eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek StackScrollState.ViewState viewState = finalState 384eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek .getViewStateForView(changingView); 385eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek if (viewState == null) { 386eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek // The position for this child was never generated, let's continue. 387eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek continue; 388eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek } 389eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek changingView.setAlpha(0); 390eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek changingView.setTranslationY(viewState.yTranslation); 391eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek changingView.setTranslationZ(viewState.zTranslation); 39239610545f0c2714a3526bc935effe57b421542d1Selim Cinek } 393572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek mHandledEvents.add(event); 394eb973565f3efc6417ca35363e4d6c642947775d8Selim Cinek mNewEvents.add(event); 395572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 396572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 397572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek } 398572bbd42a473980c2d59af80d378f6270ba6860aSelim Cinek} 399