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