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