1 /* 2 * Copyright (C) 2016 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.notification; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.PropertyValuesHolder; 22 import android.animation.ValueAnimator; 23 import android.util.Property; 24 import android.view.View; 25 import android.view.animation.Interpolator; 26 27 import com.android.systemui.Interpolators; 28 import com.android.systemui.statusbar.stack.AnimationFilter; 29 import com.android.systemui.statusbar.stack.AnimationProperties; 30 import com.android.systemui.statusbar.stack.ViewState; 31 32 /** 33 * An animator to animate properties 34 */ 35 public class PropertyAnimator { 36 37 public static <T extends View> void startAnimation(final T view, 38 AnimatableProperty animatableProperty, float newEndValue, 39 AnimationProperties properties) { 40 Property<T, Float> property = animatableProperty.getProperty(); 41 int animationStartTag = animatableProperty.getAnimationStartTag(); 42 int animationEndTag = animatableProperty.getAnimationEndTag(); 43 Float previousStartValue = ViewState.getChildTag(view, animationStartTag); 44 Float previousEndValue = ViewState.getChildTag(view, animationEndTag); 45 if (previousEndValue != null && previousEndValue == newEndValue) { 46 return; 47 } 48 int animatorTag = animatableProperty.getAnimatorTag(); 49 ValueAnimator previousAnimator = ViewState.getChildTag(view, animatorTag); 50 AnimationFilter filter = properties.getAnimationFilter(); 51 if (!filter.shouldAnimateProperty(property)) { 52 // just a local update was performed 53 if (previousAnimator != null) { 54 // we need to increase all animation keyframes of the previous animator by the 55 // relative change to the end value 56 PropertyValuesHolder[] values = previousAnimator.getValues(); 57 float relativeDiff = newEndValue - previousEndValue; 58 float newStartValue = previousStartValue + relativeDiff; 59 values[0].setFloatValues(newStartValue, newEndValue); 60 view.setTag(animationStartTag, newStartValue); 61 view.setTag(animationEndTag, newEndValue); 62 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 63 return; 64 } else { 65 // no new animation needed, let's just apply the value 66 property.set(view, newEndValue); 67 return; 68 } 69 } 70 71 Float currentValue = property.get(view); 72 ValueAnimator animator = ValueAnimator.ofFloat(currentValue, newEndValue); 73 animator.addUpdateListener( 74 animation -> property.set(view, (Float) animation.getAnimatedValue())); 75 Interpolator customInterpolator = properties.getCustomInterpolator(view, property); 76 Interpolator interpolator = customInterpolator != null ? customInterpolator 77 : Interpolators.FAST_OUT_SLOW_IN; 78 animator.setInterpolator(interpolator); 79 long newDuration = ViewState.cancelAnimatorAndGetNewDuration(properties.duration, 80 previousAnimator); 81 animator.setDuration(newDuration); 82 if (properties.delay > 0 && (previousAnimator == null 83 || previousAnimator.getAnimatedFraction() == 0)) { 84 animator.setStartDelay(properties.delay); 85 } 86 AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); 87 if (listener != null) { 88 animator.addListener(listener); 89 } 90 // remove the tag when the animation is finished 91 animator.addListener(new AnimatorListenerAdapter() { 92 @Override 93 public void onAnimationEnd(Animator animation) { 94 view.setTag(animatorTag, null); 95 view.setTag(animationStartTag, null); 96 view.setTag(animationEndTag, null); 97 } 98 }); 99 ViewState.startAnimator(animator, listener); 100 view.setTag(animatorTag, animator); 101 view.setTag(animationStartTag, currentValue); 102 view.setTag(animationEndTag, newEndValue); 103 } 104 105 public interface AnimatableProperty { 106 int getAnimationStartTag(); 107 int getAnimationEndTag(); 108 int getAnimatorTag(); 109 Property getProperty(); 110 } 111 } 112