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 copyFrom(ViewState viewState)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 initFrom(View view)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 */ applyToView(View view)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 isAnimating(View view)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 isAnimating(View view, int tag)250 private static boolean isAnimating(View view, int tag) { 251 return getChildTag(view, tag) != null; 252 } 253 isAnimating(View view, PropertyAnimator.AnimatableProperty property)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 */ animateTo(View child, AnimationProperties animationProperties)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 updateAlphaAnimation(View view)320 private void updateAlphaAnimation(View view) { 321 startAlphaAnimation(view, NO_NEW_ANIMATIONS); 322 } 323 startAlphaAnimation(final View child, AnimationProperties properties)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 updateAnimationZ(View view)402 private void updateAnimationZ(View view) { 403 startZTranslationAnimation(view, NO_NEW_ANIMATIONS); 404 } 405 updateAnimation(View view, PropertyAnimator.AnimatableProperty property, float endValue)406 private void updateAnimation(View view, PropertyAnimator.AnimatableProperty property, 407 float endValue) { 408 PropertyAnimator.startAnimation(view, property, endValue, NO_NEW_ANIMATIONS); 409 } 410 startZTranslationAnimation(final View child, AnimationProperties properties)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 updateAnimationX(View view)467 private void updateAnimationX(View view) { 468 startXTranslationAnimation(view, NO_NEW_ANIMATIONS); 469 } 470 startXTranslationAnimation(final View child, AnimationProperties properties)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 updateAnimationY(View view)532 private void updateAnimationY(View view) { 533 startYTranslationAnimation(view, NO_NEW_ANIMATIONS); 534 } 535 startYTranslationAnimation(final View child, AnimationProperties properties)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 onYTranslationAnimationFinished(View view)599 protected void onYTranslationAnimationFinished(View view) { 600 if (hidden && !gone) { 601 view.setVisibility(View.INVISIBLE); 602 } 603 } 604 startAnimator(Animator animator, AnimatorListenerAdapter listener)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 getChildTag(View child, int tag)613 public static <T> T getChildTag(View child, int tag) { 614 return (T) child.getTag(tag); 615 } 616 abortAnimation(View child, int animatorTag)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 */ cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator)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 */ getFinalTranslationY(View view)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 */ getFinalTranslationZ(View view)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 isAnimatingY(View child)676 public static boolean isAnimatingY(View child) { 677 return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null; 678 } 679 cancelAnimations(View view)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