Home | History | Annotate | Download | only in stack
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.systemui.statusbar.stack;
     18 
     19 import android.animation.Animator;
     20 import android.animation.AnimatorListenerAdapter;
     21 import android.animation.ObjectAnimator;
     22 import android.animation.PropertyValuesHolder;
     23 import android.animation.ValueAnimator;
     24 import android.app.Notification;
     25 import android.util.Property;
     26 import android.view.View;
     27 import android.view.animation.Interpolator;
     28 
     29 import com.android.systemui.Interpolators;
     30 import com.android.systemui.R;
     31 import com.android.systemui.statusbar.ExpandableView;
     32 import com.android.systemui.statusbar.NotificationShelf;
     33 import com.android.systemui.statusbar.notification.PropertyAnimator;
     34 import com.android.systemui.statusbar.policy.HeadsUpManager;
     35 
     36 /**
     37  * A state of a view. This can be used to apply a set of view properties to a view with
     38  * {@link com.android.systemui.statusbar.stack.StackScrollState} or start animations with
     39  * {@link com.android.systemui.statusbar.stack.StackStateAnimator}.
     40 */
     41 public class ViewState {
     42 
     43     /**
     44      * Some animation properties that can be used to update running animations but not creating
     45      * any new ones.
     46      */
     47     protected static final AnimationProperties NO_NEW_ANIMATIONS = new AnimationProperties() {
     48         AnimationFilter mAnimationFilter = new AnimationFilter();
     49         @Override
     50         public AnimationFilter getAnimationFilter() {
     51             return mAnimationFilter;
     52         }
     53     };
     54     private static final int TAG_ANIMATOR_TRANSLATION_X = R.id.translation_x_animator_tag;
     55     private static final int TAG_ANIMATOR_TRANSLATION_Y = R.id.translation_y_animator_tag;
     56     private static final int TAG_ANIMATOR_TRANSLATION_Z = R.id.translation_z_animator_tag;
     57     private static final int TAG_ANIMATOR_ALPHA = R.id.alpha_animator_tag;
     58     private static final int TAG_END_TRANSLATION_X = R.id.translation_x_animator_end_value_tag;
     59     private static final int TAG_END_TRANSLATION_Y = R.id.translation_y_animator_end_value_tag;
     60     private static final int TAG_END_TRANSLATION_Z = R.id.translation_z_animator_end_value_tag;
     61     private static final int TAG_END_ALPHA = R.id.alpha_animator_end_value_tag;
     62     private static final int TAG_START_TRANSLATION_X = R.id.translation_x_animator_start_value_tag;
     63     private static final int TAG_START_TRANSLATION_Y = R.id.translation_y_animator_start_value_tag;
     64     private static final int TAG_START_TRANSLATION_Z = R.id.translation_z_animator_start_value_tag;
     65     private static final int TAG_START_ALPHA = R.id.alpha_animator_start_value_tag;
     66 
     67     private static final PropertyAnimator.AnimatableProperty SCALE_X_PROPERTY
     68             = new PropertyAnimator.AnimatableProperty() {
     69 
     70         @Override
     71         public int getAnimationStartTag() {
     72             return R.id.scale_x_animator_start_value_tag;
     73         }
     74 
     75         @Override
     76         public int getAnimationEndTag() {
     77             return R.id.scale_x_animator_end_value_tag;
     78         }
     79 
     80         @Override
     81         public int getAnimatorTag() {
     82             return R.id.scale_x_animator_tag;
     83         }
     84 
     85         @Override
     86         public Property getProperty() {
     87             return View.SCALE_X;
     88         }
     89     };
     90 
     91     private static final PropertyAnimator.AnimatableProperty SCALE_Y_PROPERTY
     92             = new PropertyAnimator.AnimatableProperty() {
     93 
     94         @Override
     95         public int getAnimationStartTag() {
     96             return R.id.scale_y_animator_start_value_tag;
     97         }
     98 
     99         @Override
    100         public int getAnimationEndTag() {
    101             return R.id.scale_y_animator_end_value_tag;
    102         }
    103 
    104         @Override
    105         public int getAnimatorTag() {
    106             return R.id.scale_y_animator_tag;
    107         }
    108 
    109         @Override
    110         public Property getProperty() {
    111             return View.SCALE_Y;
    112         }
    113     };
    114 
    115     public float alpha;
    116     public float xTranslation;
    117     public float yTranslation;
    118     public float zTranslation;
    119     public boolean gone;
    120     public boolean hidden;
    121     public float scaleX = 1.0f;
    122     public float scaleY = 1.0f;
    123 
    124     public void copyFrom(ViewState viewState) {
    125         alpha = viewState.alpha;
    126         xTranslation = viewState.xTranslation;
    127         yTranslation = viewState.yTranslation;
    128         zTranslation = viewState.zTranslation;
    129         gone = viewState.gone;
    130         hidden = viewState.hidden;
    131         scaleX = viewState.scaleX;
    132         scaleY = viewState.scaleY;
    133     }
    134 
    135     public void initFrom(View view) {
    136         alpha = view.getAlpha();
    137         xTranslation = view.getTranslationX();
    138         yTranslation = view.getTranslationY();
    139         zTranslation = view.getTranslationZ();
    140         gone = view.getVisibility() == View.GONE;
    141         hidden = view.getVisibility() == View.INVISIBLE;
    142         scaleX = view.getScaleX();
    143         scaleY = view.getScaleY();
    144     }
    145 
    146     /**
    147      * Applies a {@link ViewState} to a normal view.
    148      */
    149     public void applyToView(View view) {
    150         if (this.gone) {
    151             // don't do anything with it
    152             return;
    153         }
    154 
    155         // apply xTranslation
    156         boolean animatingX = isAnimating(view, TAG_ANIMATOR_TRANSLATION_X);
    157         if (animatingX) {
    158             updateAnimationX(view);
    159         } else if (view.getTranslationX() != this.xTranslation){
    160             view.setTranslationX(this.xTranslation);
    161         }
    162 
    163         // apply yTranslation
    164         boolean animatingY = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y);
    165         if (animatingY) {
    166             updateAnimationY(view);
    167         } else if (view.getTranslationY() != this.yTranslation) {
    168             view.setTranslationY(this.yTranslation);
    169         }
    170 
    171         // apply zTranslation
    172         boolean animatingZ = isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z);
    173         if (animatingZ) {
    174             updateAnimationZ(view);
    175         } else if (view.getTranslationZ() != this.zTranslation) {
    176             view.setTranslationZ(this.zTranslation);
    177         }
    178 
    179         // apply scaleX
    180         boolean animatingScaleX = isAnimating(view, SCALE_X_PROPERTY);
    181         if (animatingScaleX) {
    182             updateAnimation(view, SCALE_X_PROPERTY, scaleX);
    183         } else if (view.getScaleX() != scaleX) {
    184             view.setScaleX(scaleX);
    185         }
    186 
    187         // apply scaleY
    188         boolean animatingScaleY = isAnimating(view, SCALE_Y_PROPERTY);
    189         if (animatingScaleY) {
    190             updateAnimation(view, SCALE_Y_PROPERTY, scaleY);
    191         } else if (view.getScaleY() != scaleY) {
    192             view.setScaleY(scaleY);
    193         }
    194 
    195         int oldVisibility = view.getVisibility();
    196         boolean becomesInvisible = this.alpha == 0.0f
    197                 || (this.hidden && (!isAnimating(view) || oldVisibility != View.VISIBLE));
    198         boolean animatingAlpha = isAnimating(view, TAG_ANIMATOR_ALPHA);
    199         if (animatingAlpha) {
    200             updateAlphaAnimation(view);
    201         } else if (view.getAlpha() != this.alpha) {
    202             // apply layer type
    203             boolean becomesFullyVisible = this.alpha == 1.0f;
    204             boolean newLayerTypeIsHardware = !becomesInvisible && !becomesFullyVisible
    205                     && view.hasOverlappingRendering();
    206             int layerType = view.getLayerType();
    207             int newLayerType = newLayerTypeIsHardware
    208                     ? View.LAYER_TYPE_HARDWARE
    209                     : View.LAYER_TYPE_NONE;
    210             if (layerType != newLayerType) {
    211                 view.setLayerType(newLayerType, null);
    212             }
    213 
    214             // apply alpha
    215             view.setAlpha(this.alpha);
    216         }
    217 
    218         // apply visibility
    219         int newVisibility = becomesInvisible ? View.INVISIBLE : View.VISIBLE;
    220         if (newVisibility != oldVisibility) {
    221             if (!(view instanceof ExpandableView) || !((ExpandableView) view).willBeGone()) {
    222                 // We don't want views to change visibility when they are animating to GONE
    223                 view.setVisibility(newVisibility);
    224             }
    225         }
    226     }
    227 
    228     public boolean isAnimating(View view) {
    229         if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_X)) {
    230             return true;
    231         }
    232         if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Y)) {
    233             return true;
    234         }
    235         if (isAnimating(view, TAG_ANIMATOR_TRANSLATION_Z)) {
    236             return true;
    237         }
    238         if (isAnimating(view, TAG_ANIMATOR_ALPHA)) {
    239             return true;
    240         }
    241         if (isAnimating(view, SCALE_X_PROPERTY)) {
    242             return true;
    243         }
    244         if (isAnimating(view, SCALE_Y_PROPERTY)) {
    245             return true;
    246         }
    247         return false;
    248     }
    249 
    250     private static boolean isAnimating(View view, int tag) {
    251         return getChildTag(view, tag) != null;
    252     }
    253 
    254     public static boolean isAnimating(View view, PropertyAnimator.AnimatableProperty property) {
    255         return getChildTag(view, property.getAnimatorTag()) != null;
    256     }
    257 
    258     /**
    259      * Start an animation to this viewstate
    260      * @param child the view to animate
    261      * @param animationProperties the properties of the animation
    262      */
    263     public void animateTo(View child, AnimationProperties animationProperties) {
    264         boolean wasVisible = child.getVisibility() == View.VISIBLE;
    265         final float alpha = this.alpha;
    266         if (!wasVisible && (alpha != 0 || child.getAlpha() != 0)
    267                 && !this.gone && !this.hidden) {
    268             child.setVisibility(View.VISIBLE);
    269         }
    270         float childAlpha = child.getAlpha();
    271         boolean alphaChanging = this.alpha != childAlpha;
    272         if (child instanceof ExpandableView) {
    273             // We don't want views to change visibility when they are animating to GONE
    274             alphaChanging &= !((ExpandableView) child).willBeGone();
    275         }
    276 
    277         // start translationX animation
    278         if (child.getTranslationX() != this.xTranslation) {
    279             startXTranslationAnimation(child, animationProperties);
    280         } else {
    281             abortAnimation(child, TAG_ANIMATOR_TRANSLATION_X);
    282         }
    283 
    284         // start translationY animation
    285         if (child.getTranslationY() != this.yTranslation) {
    286             startYTranslationAnimation(child, animationProperties);
    287         } else {
    288             abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Y);
    289         }
    290 
    291         // start translationZ animation
    292         if (child.getTranslationZ() != this.zTranslation) {
    293             startZTranslationAnimation(child, animationProperties);
    294         } else {
    295             abortAnimation(child, TAG_ANIMATOR_TRANSLATION_Z);
    296         }
    297 
    298         // start scaleX animation
    299         if (child.getScaleX() != scaleX) {
    300             PropertyAnimator.startAnimation(child, SCALE_X_PROPERTY, scaleX, animationProperties);
    301         } else {
    302             abortAnimation(child, SCALE_X_PROPERTY.getAnimatorTag());
    303         }
    304 
    305         // start scaleX animation
    306         if (child.getScaleY() != scaleY) {
    307             PropertyAnimator.startAnimation(child, SCALE_Y_PROPERTY, scaleY, animationProperties);
    308         } else {
    309             abortAnimation(child, SCALE_Y_PROPERTY.getAnimatorTag());
    310         }
    311 
    312         // start alpha animation
    313         if (alphaChanging) {
    314             startAlphaAnimation(child, animationProperties);
    315         }  else {
    316             abortAnimation(child, TAG_ANIMATOR_ALPHA);
    317         }
    318     }
    319 
    320     private void updateAlphaAnimation(View view) {
    321         startAlphaAnimation(view, NO_NEW_ANIMATIONS);
    322     }
    323 
    324     private void startAlphaAnimation(final View child, AnimationProperties properties) {
    325         Float previousStartValue = getChildTag(child,TAG_START_ALPHA);
    326         Float previousEndValue = getChildTag(child,TAG_END_ALPHA);
    327         final float newEndValue = this.alpha;
    328         if (previousEndValue != null && previousEndValue == newEndValue) {
    329             return;
    330         }
    331         ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_ALPHA);
    332         AnimationFilter filter = properties.getAnimationFilter();
    333         if (!filter.animateAlpha) {
    334             // just a local update was performed
    335             if (previousAnimator != null) {
    336                 // we need to increase all animation keyframes of the previous animator by the
    337                 // relative change to the end value
    338                 PropertyValuesHolder[] values = previousAnimator.getValues();
    339                 float relativeDiff = newEndValue - previousEndValue;
    340                 float newStartValue = previousStartValue + relativeDiff;
    341                 values[0].setFloatValues(newStartValue, newEndValue);
    342                 child.setTag(TAG_START_ALPHA, newStartValue);
    343                 child.setTag(TAG_END_ALPHA, newEndValue);
    344                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
    345                 return;
    346             } else {
    347                 // no new animation needed, let's just apply the value
    348                 child.setAlpha(newEndValue);
    349                 if (newEndValue == 0) {
    350                     child.setVisibility(View.INVISIBLE);
    351                 }
    352             }
    353         }
    354 
    355         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.ALPHA,
    356                 child.getAlpha(), newEndValue);
    357         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
    358         // Handle layer type
    359         child.setLayerType(View.LAYER_TYPE_HARDWARE, null);
    360         animator.addListener(new AnimatorListenerAdapter() {
    361             public boolean mWasCancelled;
    362 
    363             @Override
    364             public void onAnimationEnd(Animator animation) {
    365                 child.setLayerType(View.LAYER_TYPE_NONE, null);
    366                 if (newEndValue == 0 && !mWasCancelled) {
    367                     child.setVisibility(View.INVISIBLE);
    368                 }
    369                 // remove the tag when the animation is finished
    370                 child.setTag(TAG_ANIMATOR_ALPHA, null);
    371                 child.setTag(TAG_START_ALPHA, null);
    372                 child.setTag(TAG_END_ALPHA, null);
    373             }
    374 
    375             @Override
    376             public void onAnimationCancel(Animator animation) {
    377                 mWasCancelled = true;
    378             }
    379 
    380             @Override
    381             public void onAnimationStart(Animator animation) {
    382                 mWasCancelled = false;
    383             }
    384         });
    385         long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
    386         animator.setDuration(newDuration);
    387         if (properties.delay > 0 && (previousAnimator == null
    388                 || previousAnimator.getAnimatedFraction() == 0)) {
    389             animator.setStartDelay(properties.delay);
    390         }
    391         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
    392         if (listener != null) {
    393             animator.addListener(listener);
    394         }
    395 
    396         startAnimator(animator, listener);
    397         child.setTag(TAG_ANIMATOR_ALPHA, animator);
    398         child.setTag(TAG_START_ALPHA, child.getAlpha());
    399         child.setTag(TAG_END_ALPHA, newEndValue);
    400     }
    401 
    402     private void updateAnimationZ(View view) {
    403         startZTranslationAnimation(view, NO_NEW_ANIMATIONS);
    404     }
    405 
    406     private void updateAnimation(View view, PropertyAnimator.AnimatableProperty property,
    407             float endValue) {
    408         PropertyAnimator.startAnimation(view, property, endValue, NO_NEW_ANIMATIONS);
    409     }
    410 
    411     private void startZTranslationAnimation(final View child, AnimationProperties properties) {
    412         Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Z);
    413         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Z);
    414         float newEndValue = this.zTranslation;
    415         if (previousEndValue != null && previousEndValue == newEndValue) {
    416             return;
    417         }
    418         ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Z);
    419         AnimationFilter filter = properties.getAnimationFilter();
    420         if (!filter.animateZ) {
    421             // just a local update was performed
    422             if (previousAnimator != null) {
    423                 // we need to increase all animation keyframes of the previous animator by the
    424                 // relative change to the end value
    425                 PropertyValuesHolder[] values = previousAnimator.getValues();
    426                 float relativeDiff = newEndValue - previousEndValue;
    427                 float newStartValue = previousStartValue + relativeDiff;
    428                 values[0].setFloatValues(newStartValue, newEndValue);
    429                 child.setTag(TAG_START_TRANSLATION_Z, newStartValue);
    430                 child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
    431                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
    432                 return;
    433             } else {
    434                 // no new animation needed, let's just apply the value
    435                 child.setTranslationZ(newEndValue);
    436             }
    437         }
    438 
    439         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Z,
    440                 child.getTranslationZ(), newEndValue);
    441         animator.setInterpolator(Interpolators.FAST_OUT_SLOW_IN);
    442         long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
    443         animator.setDuration(newDuration);
    444         if (properties.delay > 0 && (previousAnimator == null
    445                 || previousAnimator.getAnimatedFraction() == 0)) {
    446             animator.setStartDelay(properties.delay);
    447         }
    448         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
    449         if (listener != null) {
    450             animator.addListener(listener);
    451         }
    452         // remove the tag when the animation is finished
    453         animator.addListener(new AnimatorListenerAdapter() {
    454             @Override
    455             public void onAnimationEnd(Animator animation) {
    456                 child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null);
    457                 child.setTag(TAG_START_TRANSLATION_Z, null);
    458                 child.setTag(TAG_END_TRANSLATION_Z, null);
    459             }
    460         });
    461         startAnimator(animator, listener);
    462         child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator);
    463         child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ());
    464         child.setTag(TAG_END_TRANSLATION_Z, newEndValue);
    465     }
    466 
    467     private void updateAnimationX(View view) {
    468         startXTranslationAnimation(view, NO_NEW_ANIMATIONS);
    469     }
    470 
    471     private void startXTranslationAnimation(final View child, AnimationProperties properties) {
    472         Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_X);
    473         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_X);
    474         float newEndValue = this.xTranslation;
    475         if (previousEndValue != null && previousEndValue == newEndValue) {
    476             return;
    477         }
    478         ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_X);
    479         AnimationFilter filter = properties.getAnimationFilter();
    480         if (!filter.animateX) {
    481             // just a local update was performed
    482             if (previousAnimator != null) {
    483                 // we need to increase all animation keyframes of the previous animator by the
    484                 // relative change to the end value
    485                 PropertyValuesHolder[] values = previousAnimator.getValues();
    486                 float relativeDiff = newEndValue - previousEndValue;
    487                 float newStartValue = previousStartValue + relativeDiff;
    488                 values[0].setFloatValues(newStartValue, newEndValue);
    489                 child.setTag(TAG_START_TRANSLATION_X, newStartValue);
    490                 child.setTag(TAG_END_TRANSLATION_X, newEndValue);
    491                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
    492                 return;
    493             } else {
    494                 // no new animation needed, let's just apply the value
    495                 child.setTranslationX(newEndValue);
    496                 return;
    497             }
    498         }
    499 
    500         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_X,
    501                 child.getTranslationX(), newEndValue);
    502         Interpolator customInterpolator = properties.getCustomInterpolator(child,
    503                 View.TRANSLATION_X);
    504         Interpolator interpolator =  customInterpolator != null ? customInterpolator
    505                 : Interpolators.FAST_OUT_SLOW_IN;
    506         animator.setInterpolator(interpolator);
    507         long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
    508         animator.setDuration(newDuration);
    509         if (properties.delay > 0 && (previousAnimator == null
    510                 || previousAnimator.getAnimatedFraction() == 0)) {
    511             animator.setStartDelay(properties.delay);
    512         }
    513         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
    514         if (listener != null) {
    515             animator.addListener(listener);
    516         }
    517         // remove the tag when the animation is finished
    518         animator.addListener(new AnimatorListenerAdapter() {
    519             @Override
    520             public void onAnimationEnd(Animator animation) {
    521                 child.setTag(TAG_ANIMATOR_TRANSLATION_X, null);
    522                 child.setTag(TAG_START_TRANSLATION_X, null);
    523                 child.setTag(TAG_END_TRANSLATION_X, null);
    524             }
    525         });
    526         startAnimator(animator, listener);
    527         child.setTag(TAG_ANIMATOR_TRANSLATION_X, animator);
    528         child.setTag(TAG_START_TRANSLATION_X, child.getTranslationX());
    529         child.setTag(TAG_END_TRANSLATION_X, newEndValue);
    530     }
    531 
    532     private void updateAnimationY(View view) {
    533         startYTranslationAnimation(view, NO_NEW_ANIMATIONS);
    534     }
    535 
    536     private void startYTranslationAnimation(final View child, AnimationProperties properties) {
    537         Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y);
    538         Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y);
    539         float newEndValue = this.yTranslation;
    540         if (previousEndValue != null && previousEndValue == newEndValue) {
    541             return;
    542         }
    543         ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y);
    544         AnimationFilter filter = properties.getAnimationFilter();
    545         if (!filter.shouldAnimateY(child)) {
    546             // just a local update was performed
    547             if (previousAnimator != null) {
    548                 // we need to increase all animation keyframes of the previous animator by the
    549                 // relative change to the end value
    550                 PropertyValuesHolder[] values = previousAnimator.getValues();
    551                 float relativeDiff = newEndValue - previousEndValue;
    552                 float newStartValue = previousStartValue + relativeDiff;
    553                 values[0].setFloatValues(newStartValue, newEndValue);
    554                 child.setTag(TAG_START_TRANSLATION_Y, newStartValue);
    555                 child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
    556                 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime());
    557                 return;
    558             } else {
    559                 // no new animation needed, let's just apply the value
    560                 child.setTranslationY(newEndValue);
    561                 return;
    562             }
    563         }
    564 
    565         ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y,
    566                 child.getTranslationY(), newEndValue);
    567         Interpolator customInterpolator = properties.getCustomInterpolator(child,
    568                 View.TRANSLATION_Y);
    569         Interpolator interpolator =  customInterpolator != null ? customInterpolator
    570                 : Interpolators.FAST_OUT_SLOW_IN;
    571         animator.setInterpolator(interpolator);
    572         long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator);
    573         animator.setDuration(newDuration);
    574         if (properties.delay > 0 && (previousAnimator == null
    575                 || previousAnimator.getAnimatedFraction() == 0)) {
    576             animator.setStartDelay(properties.delay);
    577         }
    578         AnimatorListenerAdapter listener = properties.getAnimationFinishListener();
    579         if (listener != null) {
    580             animator.addListener(listener);
    581         }
    582         // remove the tag when the animation is finished
    583         animator.addListener(new AnimatorListenerAdapter() {
    584             @Override
    585             public void onAnimationEnd(Animator animation) {
    586                 HeadsUpManager.setIsClickedNotification(child, false);
    587                 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null);
    588                 child.setTag(TAG_START_TRANSLATION_Y, null);
    589                 child.setTag(TAG_END_TRANSLATION_Y, null);
    590                 onYTranslationAnimationFinished(child);
    591             }
    592         });
    593         startAnimator(animator, listener);
    594         child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator);
    595         child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY());
    596         child.setTag(TAG_END_TRANSLATION_Y, newEndValue);
    597     }
    598 
    599     protected void onYTranslationAnimationFinished(View view) {
    600         if (hidden && !gone) {
    601             view.setVisibility(View.INVISIBLE);
    602         }
    603     }
    604 
    605     public static void startAnimator(Animator animator, AnimatorListenerAdapter listener) {
    606         if (listener != null) {
    607             // Even if there's a delay we'd want to notify it of the start immediately.
    608             listener.onAnimationStart(animator);
    609         }
    610         animator.start();
    611     }
    612 
    613     public static <T> T getChildTag(View child, int tag) {
    614         return (T) child.getTag(tag);
    615     }
    616 
    617     protected void abortAnimation(View child, int animatorTag) {
    618         Animator previousAnimator = getChildTag(child, animatorTag);
    619         if (previousAnimator != null) {
    620             previousAnimator.cancel();
    621         }
    622     }
    623 
    624     /**
    625      * Cancel the previous animator and get the duration of the new animation.
    626      *
    627      * @param duration the new duration
    628      * @param previousAnimator the animator which was running before
    629      * @return the new duration
    630      */
    631     public static long cancelAnimatorAndGetNewDuration(long duration,
    632             ValueAnimator previousAnimator) {
    633         long newDuration = duration;
    634         if (previousAnimator != null) {
    635             // We take either the desired length of the new animation or the remaining time of
    636             // the previous animator, whichever is longer.
    637             newDuration = Math.max(previousAnimator.getDuration()
    638                     - previousAnimator.getCurrentPlayTime(), newDuration);
    639             previousAnimator.cancel();
    640         }
    641         return newDuration;
    642     }
    643 
    644     /**
    645      * Get the end value of the yTranslation animation running on a view or the yTranslation
    646      * if no animation is running.
    647      */
    648     public static float getFinalTranslationY(View view) {
    649         if (view == null) {
    650             return 0;
    651         }
    652         ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
    653         if (yAnimator == null) {
    654             return view.getTranslationY();
    655         } else {
    656             return getChildTag(view, TAG_END_TRANSLATION_Y);
    657         }
    658     }
    659 
    660     /**
    661      * Get the end value of the zTranslation animation running on a view or the zTranslation
    662      * if no animation is running.
    663      */
    664     public static float getFinalTranslationZ(View view) {
    665         if (view == null) {
    666             return 0;
    667         }
    668         ValueAnimator zAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
    669         if (zAnimator == null) {
    670             return view.getTranslationZ();
    671         } else {
    672             return getChildTag(view, TAG_END_TRANSLATION_Z);
    673         }
    674     }
    675 
    676     public static boolean isAnimatingY(View child) {
    677         return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null;
    678     }
    679 
    680     public void cancelAnimations(View view) {
    681         Animator animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X);
    682         if (animator != null) {
    683             animator.cancel();
    684         }
    685         animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y);
    686         if (animator != null) {
    687             animator.cancel();
    688         }
    689         animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z);
    690         if (animator != null) {
    691             animator.cancel();
    692         }
    693         animator = getChildTag(view, TAG_ANIMATOR_ALPHA);
    694         if (animator != null) {
    695             animator.cancel();
    696         }
    697     }
    698 }
    699