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.Interpolators; 30 import com.android.systemui.R; 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(); 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 if (listener != null) { 455 animator.addListener(listener); 456 } 457 // remove the tag when the animation is finished 458 animator.addListener(new AnimatorListenerAdapter() { 459 @Override 460 public void onAnimationEnd(Animator animation) { 461 child.setTag(TAG_ANIMATOR_TRANSLATION_Z, null); 462 child.setTag(TAG_START_TRANSLATION_Z, null); 463 child.setTag(TAG_END_TRANSLATION_Z, null); 464 } 465 }); 466 startAnimator(animator, listener); 467 child.setTag(TAG_ANIMATOR_TRANSLATION_Z, animator); 468 child.setTag(TAG_START_TRANSLATION_Z, child.getTranslationZ()); 469 child.setTag(TAG_END_TRANSLATION_Z, newEndValue); 470 } 471 updateAnimationX(View view)472 private void updateAnimationX(View view) { 473 startXTranslationAnimation(view, NO_NEW_ANIMATIONS); 474 } 475 startXTranslationAnimation(final View child, AnimationProperties properties)476 private void startXTranslationAnimation(final View child, AnimationProperties properties) { 477 Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_X); 478 Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_X); 479 float newEndValue = this.xTranslation; 480 if (previousEndValue != null && previousEndValue == newEndValue) { 481 return; 482 } 483 ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_X); 484 AnimationFilter filter = properties.getAnimationFilter(); 485 if (!filter.animateX) { 486 // just a local update was performed 487 if (previousAnimator != null) { 488 // we need to increase all animation keyframes of the previous animator by the 489 // relative change to the end value 490 PropertyValuesHolder[] values = previousAnimator.getValues(); 491 float relativeDiff = newEndValue - previousEndValue; 492 float newStartValue = previousStartValue + relativeDiff; 493 values[0].setFloatValues(newStartValue, newEndValue); 494 child.setTag(TAG_START_TRANSLATION_X, newStartValue); 495 child.setTag(TAG_END_TRANSLATION_X, newEndValue); 496 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 497 return; 498 } else { 499 // no new animation needed, let's just apply the value 500 child.setTranslationX(newEndValue); 501 return; 502 } 503 } 504 505 ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_X, 506 child.getTranslationX(), newEndValue); 507 Interpolator customInterpolator = properties.getCustomInterpolator(child, 508 View.TRANSLATION_X); 509 Interpolator interpolator = customInterpolator != null ? customInterpolator 510 : Interpolators.FAST_OUT_SLOW_IN; 511 animator.setInterpolator(interpolator); 512 long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); 513 animator.setDuration(newDuration); 514 if (properties.delay > 0 && (previousAnimator == null 515 || previousAnimator.getAnimatedFraction() == 0)) { 516 animator.setStartDelay(properties.delay); 517 } 518 AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); 519 if (listener != null) { 520 animator.addListener(listener); 521 } 522 // remove the tag when the animation is finished 523 animator.addListener(new AnimatorListenerAdapter() { 524 @Override 525 public void onAnimationEnd(Animator animation) { 526 child.setTag(TAG_ANIMATOR_TRANSLATION_X, null); 527 child.setTag(TAG_START_TRANSLATION_X, null); 528 child.setTag(TAG_END_TRANSLATION_X, null); 529 } 530 }); 531 startAnimator(animator, listener); 532 child.setTag(TAG_ANIMATOR_TRANSLATION_X, animator); 533 child.setTag(TAG_START_TRANSLATION_X, child.getTranslationX()); 534 child.setTag(TAG_END_TRANSLATION_X, newEndValue); 535 } 536 updateAnimationY(View view)537 private void updateAnimationY(View view) { 538 startYTranslationAnimation(view, NO_NEW_ANIMATIONS); 539 } 540 startYTranslationAnimation(final View child, AnimationProperties properties)541 private void startYTranslationAnimation(final View child, AnimationProperties properties) { 542 Float previousStartValue = getChildTag(child,TAG_START_TRANSLATION_Y); 543 Float previousEndValue = getChildTag(child,TAG_END_TRANSLATION_Y); 544 float newEndValue = this.yTranslation; 545 if (previousEndValue != null && previousEndValue == newEndValue) { 546 return; 547 } 548 ObjectAnimator previousAnimator = getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y); 549 AnimationFilter filter = properties.getAnimationFilter(); 550 if (!filter.shouldAnimateY(child)) { 551 // just a local update was performed 552 if (previousAnimator != null) { 553 // we need to increase all animation keyframes of the previous animator by the 554 // relative change to the end value 555 PropertyValuesHolder[] values = previousAnimator.getValues(); 556 float relativeDiff = newEndValue - previousEndValue; 557 float newStartValue = previousStartValue + relativeDiff; 558 values[0].setFloatValues(newStartValue, newEndValue); 559 child.setTag(TAG_START_TRANSLATION_Y, newStartValue); 560 child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 561 previousAnimator.setCurrentPlayTime(previousAnimator.getCurrentPlayTime()); 562 return; 563 } else { 564 // no new animation needed, let's just apply the value 565 child.setTranslationY(newEndValue); 566 return; 567 } 568 } 569 570 ObjectAnimator animator = ObjectAnimator.ofFloat(child, View.TRANSLATION_Y, 571 child.getTranslationY(), newEndValue); 572 Interpolator customInterpolator = properties.getCustomInterpolator(child, 573 View.TRANSLATION_Y); 574 Interpolator interpolator = customInterpolator != null ? customInterpolator 575 : Interpolators.FAST_OUT_SLOW_IN; 576 animator.setInterpolator(interpolator); 577 long newDuration = cancelAnimatorAndGetNewDuration(properties.duration, previousAnimator); 578 animator.setDuration(newDuration); 579 if (properties.delay > 0 && (previousAnimator == null 580 || previousAnimator.getAnimatedFraction() == 0)) { 581 animator.setStartDelay(properties.delay); 582 } 583 AnimatorListenerAdapter listener = properties.getAnimationFinishListener(); 584 if (listener != null) { 585 animator.addListener(listener); 586 } 587 // remove the tag when the animation is finished 588 animator.addListener(new AnimatorListenerAdapter() { 589 @Override 590 public void onAnimationEnd(Animator animation) { 591 HeadsUpUtil.setIsClickedHeadsUpNotification(child, false); 592 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, null); 593 child.setTag(TAG_START_TRANSLATION_Y, null); 594 child.setTag(TAG_END_TRANSLATION_Y, null); 595 onYTranslationAnimationFinished(child); 596 } 597 }); 598 startAnimator(animator, listener); 599 child.setTag(TAG_ANIMATOR_TRANSLATION_Y, animator); 600 child.setTag(TAG_START_TRANSLATION_Y, child.getTranslationY()); 601 child.setTag(TAG_END_TRANSLATION_Y, newEndValue); 602 } 603 onYTranslationAnimationFinished(View view)604 protected void onYTranslationAnimationFinished(View view) { 605 if (hidden && !gone) { 606 view.setVisibility(View.INVISIBLE); 607 } 608 } 609 startAnimator(Animator animator, AnimatorListenerAdapter listener)610 public static void startAnimator(Animator animator, AnimatorListenerAdapter listener) { 611 if (listener != null) { 612 // Even if there's a delay we'd want to notify it of the start immediately. 613 listener.onAnimationStart(animator); 614 } 615 animator.start(); 616 } 617 getChildTag(View child, int tag)618 public static <T> T getChildTag(View child, int tag) { 619 return (T) child.getTag(tag); 620 } 621 abortAnimation(View child, int animatorTag)622 protected void abortAnimation(View child, int animatorTag) { 623 Animator previousAnimator = getChildTag(child, animatorTag); 624 if (previousAnimator != null) { 625 previousAnimator.cancel(); 626 } 627 } 628 629 /** 630 * Cancel the previous animator and get the duration of the new animation. 631 * 632 * @param duration the new duration 633 * @param previousAnimator the animator which was running before 634 * @return the new duration 635 */ cancelAnimatorAndGetNewDuration(long duration, ValueAnimator previousAnimator)636 public static long cancelAnimatorAndGetNewDuration(long duration, 637 ValueAnimator previousAnimator) { 638 long newDuration = duration; 639 if (previousAnimator != null) { 640 // We take either the desired length of the new animation or the remaining time of 641 // the previous animator, whichever is longer. 642 newDuration = Math.max(previousAnimator.getDuration() 643 - previousAnimator.getCurrentPlayTime(), newDuration); 644 previousAnimator.cancel(); 645 } 646 return newDuration; 647 } 648 649 /** 650 * Get the end value of the xTranslation animation running on a view or the xTranslation 651 * if no animation is running. 652 */ getFinalTranslationX(View view)653 public static float getFinalTranslationX(View view) { 654 if (view == null) { 655 return 0; 656 } 657 ValueAnimator xAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X); 658 if (xAnimator == null) { 659 return view.getTranslationX(); 660 } else { 661 return getChildTag(view, TAG_END_TRANSLATION_X); 662 } 663 } 664 665 /** 666 * Get the end value of the yTranslation animation running on a view or the yTranslation 667 * if no animation is running. 668 */ getFinalTranslationY(View view)669 public static float getFinalTranslationY(View view) { 670 if (view == null) { 671 return 0; 672 } 673 ValueAnimator yAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y); 674 if (yAnimator == null) { 675 return view.getTranslationY(); 676 } else { 677 return getChildTag(view, TAG_END_TRANSLATION_Y); 678 } 679 } 680 681 /** 682 * Get the end value of the zTranslation animation running on a view or the zTranslation 683 * if no animation is running. 684 */ getFinalTranslationZ(View view)685 public static float getFinalTranslationZ(View view) { 686 if (view == null) { 687 return 0; 688 } 689 ValueAnimator zAnimator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z); 690 if (zAnimator == null) { 691 return view.getTranslationZ(); 692 } else { 693 return getChildTag(view, TAG_END_TRANSLATION_Z); 694 } 695 } 696 isAnimatingY(View child)697 public static boolean isAnimatingY(View child) { 698 return getChildTag(child, TAG_ANIMATOR_TRANSLATION_Y) != null; 699 } 700 cancelAnimations(View view)701 public void cancelAnimations(View view) { 702 Animator animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_X); 703 if (animator != null) { 704 animator.cancel(); 705 } 706 animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Y); 707 if (animator != null) { 708 animator.cancel(); 709 } 710 animator = getChildTag(view, TAG_ANIMATOR_TRANSLATION_Z); 711 if (animator != null) { 712 animator.cancel(); 713 } 714 animator = getChildTag(view, TAG_ANIMATOR_ALPHA); 715 if (animator != null) { 716 animator.cancel(); 717 } 718 } 719 720 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)721 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 722 StringBuilder result = new StringBuilder(); 723 result.append("ViewState { "); 724 725 boolean first = true; 726 Class currentClass = this.getClass(); 727 while (currentClass != null) { 728 Field[] fields = currentClass.getDeclaredFields(); 729 // Print field names paired with their values 730 for (Field field : fields) { 731 int modifiers = field.getModifiers(); 732 if (Modifier.isStatic(modifiers) || field.isSynthetic() 733 || Modifier.isTransient(modifiers)) { 734 continue; 735 } 736 if (!first) { 737 result.append(", "); 738 } 739 try { 740 result.append(field.getName()); 741 result.append(": "); 742 //requires access to private field: 743 field.setAccessible(true); 744 result.append(field.get(this)); 745 } catch (IllegalAccessException ex) { 746 } 747 first = false; 748 } 749 currentClass = currentClass.getSuperclass(); 750 } 751 result.append(" }"); 752 pw.print(result); 753 } 754 } 755