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