1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.notification; 18 19 import android.util.Pools; 20 import android.view.View; 21 import android.view.animation.Interpolator; 22 import android.widget.ImageView; 23 import android.widget.ProgressBar; 24 import android.widget.TextView; 25 26 import com.android.internal.widget.MessagingImageMessage; 27 import com.android.internal.widget.MessagingPropertyAnimator; 28 import com.android.internal.widget.ViewClippingUtil; 29 import com.android.systemui.Interpolators; 30 import com.android.systemui.R; 31 import com.android.systemui.statusbar.CrossFadeHelper; 32 import com.android.systemui.statusbar.TransformableView; 33 import com.android.systemui.statusbar.ViewTransformationHelper; 34 import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; 35 36 /** 37 * A transform state of a view. 38 */ 39 public class TransformState { 40 41 public static final int TRANSFORM_X = 0x1; 42 public static final int TRANSFORM_Y = 0x10; 43 public static final int TRANSFORM_ALL = TRANSFORM_X | TRANSFORM_Y; 44 45 private static final float UNDEFINED = -1f; 46 private static final int TRANSFORMATION_START_X = R.id.transformation_start_x_tag; 47 private static final int TRANSFORMATION_START_Y = R.id.transformation_start_y_tag; 48 private static final int TRANSFORMATION_START_SCLALE_X = R.id.transformation_start_scale_x_tag; 49 private static final int TRANSFORMATION_START_SCLALE_Y = R.id.transformation_start_scale_y_tag; 50 private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40); 51 private static ViewClippingUtil.ClippingParameters CLIPPING_PARAMETERS 52 = new ViewClippingUtil.ClippingParameters() { 53 @Override 54 public boolean shouldFinish(View view) { 55 if (view instanceof ExpandableNotificationRow) { 56 ExpandableNotificationRow row = (ExpandableNotificationRow) view; 57 return !row.isChildInGroup(); 58 } 59 return false; 60 } 61 62 @Override 63 public void onClippingStateChanged(View view, boolean isClipping) { 64 if (view instanceof ExpandableNotificationRow) { 65 ExpandableNotificationRow row = (ExpandableNotificationRow) view; 66 if (isClipping) { 67 row.setClipToActualHeight(true); 68 } else if (row.isChildInGroup()) { 69 row.setClipToActualHeight(false); 70 } 71 } 72 } 73 }; 74 75 protected View mTransformedView; 76 protected TransformInfo mTransformInfo; 77 private int[] mOwnPosition = new int[2]; 78 private boolean mSameAsAny; 79 private float mTransformationEndY = UNDEFINED; 80 private float mTransformationEndX = UNDEFINED; 81 protected Interpolator mDefaultInterpolator = Interpolators.FAST_OUT_SLOW_IN; 82 initFrom(View view, TransformInfo transformInfo)83 public void initFrom(View view, TransformInfo transformInfo) { 84 mTransformedView = view; 85 mTransformInfo = transformInfo; 86 } 87 88 /** 89 * Transforms the {@link #mTransformedView} from the given transformviewstate 90 * @param otherState the state to transform from 91 * @param transformationAmount how much to transform 92 */ transformViewFrom(TransformState otherState, float transformationAmount)93 public void transformViewFrom(TransformState otherState, float transformationAmount) { 94 mTransformedView.animate().cancel(); 95 if (sameAs(otherState)) { 96 ensureVisible(); 97 } else { 98 CrossFadeHelper.fadeIn(mTransformedView, transformationAmount); 99 } 100 transformViewFullyFrom(otherState, transformationAmount); 101 } 102 ensureVisible()103 protected void ensureVisible() { 104 if (mTransformedView.getVisibility() == View.INVISIBLE 105 || mTransformedView.getAlpha() != 1.0f) { 106 // We have the same content, lets show ourselves 107 mTransformedView.setAlpha(1.0f); 108 mTransformedView.setVisibility(View.VISIBLE); 109 } 110 } 111 transformViewFullyFrom(TransformState otherState, float transformationAmount)112 public void transformViewFullyFrom(TransformState otherState, float transformationAmount) { 113 transformViewFrom(otherState, TRANSFORM_ALL, null, transformationAmount); 114 } 115 transformViewFullyFrom(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)116 public void transformViewFullyFrom(TransformState otherState, 117 ViewTransformationHelper.CustomTransformation customTransformation, 118 float transformationAmount) { 119 transformViewFrom(otherState, TRANSFORM_ALL, customTransformation, transformationAmount); 120 } 121 transformViewVerticalFrom(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)122 public void transformViewVerticalFrom(TransformState otherState, 123 ViewTransformationHelper.CustomTransformation customTransformation, 124 float transformationAmount) { 125 transformViewFrom(otherState, TRANSFORM_Y, customTransformation, transformationAmount); 126 } 127 transformViewVerticalFrom(TransformState otherState, float transformationAmount)128 public void transformViewVerticalFrom(TransformState otherState, float transformationAmount) { 129 transformViewFrom(otherState, TRANSFORM_Y, null, transformationAmount); 130 } 131 transformViewFrom(TransformState otherState, int transformationFlags, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)132 protected void transformViewFrom(TransformState otherState, int transformationFlags, 133 ViewTransformationHelper.CustomTransformation customTransformation, 134 float transformationAmount) { 135 final View transformedView = mTransformedView; 136 boolean transformX = (transformationFlags & TRANSFORM_X) != 0; 137 boolean transformY = (transformationFlags & TRANSFORM_Y) != 0; 138 int viewHeight = getViewHeight(); 139 int otherHeight = otherState.getViewHeight(); 140 boolean differentHeight = otherHeight != viewHeight && otherHeight != 0 && viewHeight != 0; 141 int viewWidth = getViewWidth(); 142 int otherWidth = otherState.getViewWidth(); 143 boolean differentWidth = otherWidth != viewWidth && otherWidth != 0 && viewWidth != 0; 144 boolean transformScale = transformScale(otherState) && (differentHeight || differentWidth); 145 // lets animate the positions correctly 146 if (transformationAmount == 0.0f 147 || transformX && getTransformationStartX() == UNDEFINED 148 || transformY && getTransformationStartY() == UNDEFINED 149 || transformScale && getTransformationStartScaleX() == UNDEFINED && differentWidth 150 || transformScale && getTransformationStartScaleY() == UNDEFINED 151 && differentHeight) { 152 int[] otherPosition; 153 if (transformationAmount != 0.0f) { 154 otherPosition = otherState.getLaidOutLocationOnScreen(); 155 } else { 156 otherPosition = otherState.getLocationOnScreen(); 157 } 158 int[] ownStablePosition = getLaidOutLocationOnScreen(); 159 if (customTransformation == null 160 || !customTransformation.initTransformation(this, otherState)) { 161 if (transformX) { 162 setTransformationStartX(otherPosition[0] - ownStablePosition[0]); 163 } 164 if (transformY) { 165 setTransformationStartY(otherPosition[1] - ownStablePosition[1]); 166 } 167 // we also want to animate the scale if we're the same 168 View otherView = otherState.getTransformedView(); 169 if (transformScale && differentWidth) { 170 setTransformationStartScaleX(otherWidth * otherView.getScaleX() 171 / (float) viewWidth); 172 transformedView.setPivotX(0); 173 } else { 174 setTransformationStartScaleX(UNDEFINED); 175 } 176 if (transformScale && differentHeight) { 177 setTransformationStartScaleY(otherHeight * otherView.getScaleY() 178 / (float) viewHeight); 179 transformedView.setPivotY(0); 180 } else { 181 setTransformationStartScaleY(UNDEFINED); 182 } 183 } 184 if (!transformX) { 185 setTransformationStartX(UNDEFINED); 186 } 187 if (!transformY) { 188 setTransformationStartY(UNDEFINED); 189 } 190 if (!transformScale) { 191 setTransformationStartScaleX(UNDEFINED); 192 setTransformationStartScaleY(UNDEFINED); 193 } 194 setClippingDeactivated(transformedView, true); 195 } 196 float interpolatedValue = mDefaultInterpolator.getInterpolation( 197 transformationAmount); 198 if (transformX) { 199 float interpolation = interpolatedValue; 200 if (customTransformation != null) { 201 Interpolator customInterpolator = 202 customTransformation.getCustomInterpolator(TRANSFORM_X, true /* isFrom */); 203 if (customInterpolator != null) { 204 interpolation = customInterpolator.getInterpolation(transformationAmount); 205 } 206 } 207 transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(), 208 0.0f, 209 interpolation)); 210 } 211 if (transformY) { 212 float interpolation = interpolatedValue; 213 if (customTransformation != null) { 214 Interpolator customInterpolator = 215 customTransformation.getCustomInterpolator(TRANSFORM_Y, true /* isFrom */); 216 if (customInterpolator != null) { 217 interpolation = customInterpolator.getInterpolation(transformationAmount); 218 } 219 } 220 transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(), 221 0.0f, 222 interpolation)); 223 } 224 if (transformScale) { 225 float transformationStartScaleX = getTransformationStartScaleX(); 226 if (transformationStartScaleX != UNDEFINED) { 227 transformedView.setScaleX( 228 NotificationUtils.interpolate(transformationStartScaleX, 229 1.0f, 230 interpolatedValue)); 231 } 232 float transformationStartScaleY = getTransformationStartScaleY(); 233 if (transformationStartScaleY != UNDEFINED) { 234 transformedView.setScaleY( 235 NotificationUtils.interpolate(transformationStartScaleY, 236 1.0f, 237 interpolatedValue)); 238 } 239 } 240 } 241 getViewWidth()242 protected int getViewWidth() { 243 return mTransformedView.getWidth(); 244 } 245 getViewHeight()246 protected int getViewHeight() { 247 return mTransformedView.getHeight(); 248 } 249 transformScale(TransformState otherState)250 protected boolean transformScale(TransformState otherState) { 251 return false; 252 } 253 254 /** 255 * Transforms the {@link #mTransformedView} to the given transformviewstate 256 * @param otherState the state to transform from 257 * @param transformationAmount how much to transform 258 * @return whether an animation was started 259 */ transformViewTo(TransformState otherState, float transformationAmount)260 public boolean transformViewTo(TransformState otherState, float transformationAmount) { 261 mTransformedView.animate().cancel(); 262 if (sameAs(otherState)) { 263 // We have the same text, lets show ourselfs 264 if (mTransformedView.getVisibility() == View.VISIBLE) { 265 mTransformedView.setAlpha(0.0f); 266 mTransformedView.setVisibility(View.INVISIBLE); 267 } 268 return false; 269 } else { 270 CrossFadeHelper.fadeOut(mTransformedView, transformationAmount); 271 } 272 transformViewFullyTo(otherState, transformationAmount); 273 return true; 274 } 275 transformViewFullyTo(TransformState otherState, float transformationAmount)276 public void transformViewFullyTo(TransformState otherState, float transformationAmount) { 277 transformViewTo(otherState, TRANSFORM_ALL, null, transformationAmount); 278 } 279 transformViewFullyTo(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)280 public void transformViewFullyTo(TransformState otherState, 281 ViewTransformationHelper.CustomTransformation customTransformation, 282 float transformationAmount) { 283 transformViewTo(otherState, TRANSFORM_ALL, customTransformation, transformationAmount); 284 } 285 transformViewVerticalTo(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)286 public void transformViewVerticalTo(TransformState otherState, 287 ViewTransformationHelper.CustomTransformation customTransformation, 288 float transformationAmount) { 289 transformViewTo(otherState, TRANSFORM_Y, customTransformation, transformationAmount); 290 } 291 transformViewVerticalTo(TransformState otherState, float transformationAmount)292 public void transformViewVerticalTo(TransformState otherState, float transformationAmount) { 293 transformViewTo(otherState, TRANSFORM_Y, null, transformationAmount); 294 } 295 transformViewTo(TransformState otherState, int transformationFlags, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)296 private void transformViewTo(TransformState otherState, int transformationFlags, 297 ViewTransformationHelper.CustomTransformation customTransformation, 298 float transformationAmount) { 299 // lets animate the positions correctly 300 301 final View transformedView = mTransformedView; 302 boolean transformX = (transformationFlags & TRANSFORM_X) != 0; 303 boolean transformY = (transformationFlags & TRANSFORM_Y) != 0; 304 boolean transformScale = transformScale(otherState); 305 // lets animate the positions correctly 306 if (transformationAmount == 0.0f) { 307 if (transformX) { 308 float transformationStartX = getTransformationStartX(); 309 float start = transformationStartX != UNDEFINED ? transformationStartX 310 : transformedView.getTranslationX(); 311 setTransformationStartX(start); 312 } 313 if (transformY) { 314 float transformationStartY = getTransformationStartY(); 315 float start = transformationStartY != UNDEFINED ? transformationStartY 316 : transformedView.getTranslationY(); 317 setTransformationStartY(start); 318 } 319 View otherView = otherState.getTransformedView(); 320 if (transformScale && otherState.getViewWidth() != getViewWidth()) { 321 setTransformationStartScaleX(transformedView.getScaleX()); 322 transformedView.setPivotX(0); 323 } else { 324 setTransformationStartScaleX(UNDEFINED); 325 } 326 if (transformScale && otherState.getViewHeight() != getViewHeight()) { 327 setTransformationStartScaleY(transformedView.getScaleY()); 328 transformedView.setPivotY(0); 329 } else { 330 setTransformationStartScaleY(UNDEFINED); 331 } 332 setClippingDeactivated(transformedView, true); 333 } 334 float interpolatedValue = mDefaultInterpolator.getInterpolation( 335 transformationAmount); 336 int[] otherStablePosition = otherState.getLaidOutLocationOnScreen(); 337 int[] ownPosition = getLaidOutLocationOnScreen(); 338 if (transformX) { 339 float endX = otherStablePosition[0] - ownPosition[0]; 340 float interpolation = interpolatedValue; 341 if (customTransformation != null) { 342 if (customTransformation.customTransformTarget(this, otherState)) { 343 endX = mTransformationEndX; 344 } 345 Interpolator customInterpolator = 346 customTransformation.getCustomInterpolator(TRANSFORM_X, false /* isFrom */); 347 if (customInterpolator != null) { 348 interpolation = customInterpolator.getInterpolation(transformationAmount); 349 } 350 } 351 transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(), 352 endX, 353 interpolation)); 354 } 355 if (transformY) { 356 float endY = otherStablePosition[1] - ownPosition[1]; 357 float interpolation = interpolatedValue; 358 if (customTransformation != null) { 359 if (customTransformation.customTransformTarget(this, otherState)) { 360 endY = mTransformationEndY; 361 } 362 Interpolator customInterpolator = 363 customTransformation.getCustomInterpolator(TRANSFORM_Y, false /* isFrom */); 364 if (customInterpolator != null) { 365 interpolation = customInterpolator.getInterpolation(transformationAmount); 366 } 367 } 368 transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(), 369 endY, 370 interpolation)); 371 } 372 if (transformScale) { 373 View otherView = otherState.getTransformedView(); 374 float transformationStartScaleX = getTransformationStartScaleX(); 375 if (transformationStartScaleX != UNDEFINED) { 376 transformedView.setScaleX( 377 NotificationUtils.interpolate(transformationStartScaleX, 378 (otherState.getViewWidth() / (float) getViewWidth()), 379 interpolatedValue)); 380 } 381 float transformationStartScaleY = getTransformationStartScaleY(); 382 if (transformationStartScaleY != UNDEFINED) { 383 transformedView.setScaleY( 384 NotificationUtils.interpolate(transformationStartScaleY, 385 (otherState.getViewHeight() / (float) getViewHeight()), 386 interpolatedValue)); 387 } 388 } 389 } 390 setClippingDeactivated(final View transformedView, boolean deactivated)391 protected void setClippingDeactivated(final View transformedView, boolean deactivated) { 392 ViewClippingUtil.setClippingDeactivated(transformedView, deactivated, CLIPPING_PARAMETERS); 393 } 394 getLaidOutLocationOnScreen()395 public int[] getLaidOutLocationOnScreen() { 396 int[] location = getLocationOnScreen(); 397 // remove translation 398 location[0] -= mTransformedView.getTranslationX(); 399 location[1] -= mTransformedView.getTranslationY(); 400 return location; 401 } 402 getLocationOnScreen()403 public int[] getLocationOnScreen() { 404 mTransformedView.getLocationOnScreen(mOwnPosition); 405 406 // remove scale 407 mOwnPosition[0] -= (1.0f - mTransformedView.getScaleX()) * mTransformedView.getPivotX(); 408 mOwnPosition[1] -= (1.0f - mTransformedView.getScaleY()) * mTransformedView.getPivotY(); 409 410 // Remove local translations 411 mOwnPosition[1] -= MessagingPropertyAnimator.getTop(mTransformedView) 412 - MessagingPropertyAnimator.getLayoutTop(mTransformedView); 413 return mOwnPosition; 414 } 415 sameAs(TransformState otherState)416 protected boolean sameAs(TransformState otherState) { 417 return mSameAsAny; 418 } 419 appear(float transformationAmount, TransformableView otherView)420 public void appear(float transformationAmount, TransformableView otherView) { 421 // There's no other view, lets fade us in 422 // Certain views need to prepare the fade in and make sure its children are 423 // completely visible. An example is the notification header. 424 if (transformationAmount == 0.0f) { 425 prepareFadeIn(); 426 } 427 CrossFadeHelper.fadeIn(mTransformedView, transformationAmount); 428 } 429 disappear(float transformationAmount, TransformableView otherView)430 public void disappear(float transformationAmount, TransformableView otherView) { 431 CrossFadeHelper.fadeOut(mTransformedView, transformationAmount); 432 } 433 createFrom(View view, TransformInfo transformInfo)434 public static TransformState createFrom(View view, 435 TransformInfo transformInfo) { 436 if (view instanceof TextView) { 437 TextViewTransformState result = TextViewTransformState.obtain(); 438 result.initFrom(view, transformInfo); 439 return result; 440 } 441 if (view.getId() == com.android.internal.R.id.actions_container) { 442 ActionListTransformState result = ActionListTransformState.obtain(); 443 result.initFrom(view, transformInfo); 444 return result; 445 } 446 if (view.getId() == com.android.internal.R.id.notification_messaging) { 447 MessagingLayoutTransformState result = MessagingLayoutTransformState.obtain(); 448 result.initFrom(view, transformInfo); 449 return result; 450 } 451 if (view instanceof MessagingImageMessage) { 452 MessagingImageTransformState result = MessagingImageTransformState.obtain(); 453 result.initFrom(view, transformInfo); 454 return result; 455 } 456 if (view instanceof ImageView) { 457 ImageTransformState result = ImageTransformState.obtain(); 458 result.initFrom(view, transformInfo); 459 if (view.getId() == com.android.internal.R.id.reply_icon_action) { 460 ((TransformState) result).setIsSameAsAnyView(true); 461 } 462 return result; 463 } 464 if (view instanceof ProgressBar) { 465 ProgressTransformState result = ProgressTransformState.obtain(); 466 result.initFrom(view, transformInfo); 467 return result; 468 } 469 TransformState result = obtain(); 470 result.initFrom(view, transformInfo); 471 return result; 472 } 473 setIsSameAsAnyView(boolean sameAsAny)474 public void setIsSameAsAnyView(boolean sameAsAny) { 475 mSameAsAny = sameAsAny; 476 } 477 recycle()478 public void recycle() { 479 reset(); 480 if (getClass() == TransformState.class) { 481 sInstancePool.release(this); 482 } 483 } 484 setTransformationEndY(float transformationEndY)485 public void setTransformationEndY(float transformationEndY) { 486 mTransformationEndY = transformationEndY; 487 } 488 setTransformationEndX(float transformationEndX)489 public void setTransformationEndX(float transformationEndX) { 490 mTransformationEndX = transformationEndX; 491 } 492 getTransformationStartX()493 public float getTransformationStartX() { 494 Object tag = mTransformedView.getTag(TRANSFORMATION_START_X); 495 return tag == null ? UNDEFINED : (float) tag; 496 } 497 getTransformationStartY()498 public float getTransformationStartY() { 499 Object tag = mTransformedView.getTag(TRANSFORMATION_START_Y); 500 return tag == null ? UNDEFINED : (float) tag; 501 } 502 getTransformationStartScaleX()503 public float getTransformationStartScaleX() { 504 Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_X); 505 return tag == null ? UNDEFINED : (float) tag; 506 } 507 getTransformationStartScaleY()508 public float getTransformationStartScaleY() { 509 Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_Y); 510 return tag == null ? UNDEFINED : (float) tag; 511 } 512 setTransformationStartX(float transformationStartX)513 public void setTransformationStartX(float transformationStartX) { 514 mTransformedView.setTag(TRANSFORMATION_START_X, transformationStartX); 515 } 516 setTransformationStartY(float transformationStartY)517 public void setTransformationStartY(float transformationStartY) { 518 mTransformedView.setTag(TRANSFORMATION_START_Y, transformationStartY); 519 } 520 setTransformationStartScaleX(float startScaleX)521 private void setTransformationStartScaleX(float startScaleX) { 522 mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, startScaleX); 523 } 524 setTransformationStartScaleY(float startScaleY)525 private void setTransformationStartScaleY(float startScaleY) { 526 mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, startScaleY); 527 } 528 reset()529 protected void reset() { 530 mTransformedView = null; 531 mTransformInfo = null; 532 mSameAsAny = false; 533 mTransformationEndX = UNDEFINED; 534 mTransformationEndY = UNDEFINED; 535 mDefaultInterpolator = Interpolators.FAST_OUT_SLOW_IN; 536 } 537 setVisible(boolean visible, boolean force)538 public void setVisible(boolean visible, boolean force) { 539 if (!force && mTransformedView.getVisibility() == View.GONE) { 540 return; 541 } 542 if (mTransformedView.getVisibility() != View.GONE) { 543 mTransformedView.setVisibility(visible ? View.VISIBLE : View.INVISIBLE); 544 } 545 mTransformedView.animate().cancel(); 546 mTransformedView.setAlpha(visible ? 1.0f : 0.0f); 547 resetTransformedView(); 548 } 549 prepareFadeIn()550 public void prepareFadeIn() { 551 resetTransformedView(); 552 } 553 resetTransformedView()554 protected void resetTransformedView() { 555 mTransformedView.setTranslationX(0); 556 mTransformedView.setTranslationY(0); 557 mTransformedView.setScaleX(1.0f); 558 mTransformedView.setScaleY(1.0f); 559 setClippingDeactivated(mTransformedView, false); 560 abortTransformation(); 561 } 562 abortTransformation()563 public void abortTransformation() { 564 mTransformedView.setTag(TRANSFORMATION_START_X, UNDEFINED); 565 mTransformedView.setTag(TRANSFORMATION_START_Y, UNDEFINED); 566 mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, UNDEFINED); 567 mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, UNDEFINED); 568 } 569 obtain()570 public static TransformState obtain() { 571 TransformState instance = sInstancePool.acquire(); 572 if (instance != null) { 573 return instance; 574 } 575 return new TransformState(); 576 } 577 getTransformedView()578 public View getTransformedView() { 579 return mTransformedView; 580 } 581 setDefaultInterpolator(Interpolator interpolator)582 public void setDefaultInterpolator(Interpolator interpolator) { 583 mDefaultInterpolator = interpolator; 584 } 585 586 public interface TransformInfo { isAnimating()587 boolean isAnimating(); 588 } 589 } 590