• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ArraySet;
20 import android.util.Pools;
21 import android.view.View;
22 import android.view.ViewGroup;
23 import android.view.ViewParent;
24 import android.view.animation.Interpolator;
25 import android.widget.ImageView;
26 import android.widget.ProgressBar;
27 import android.widget.TextView;
28 
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.ExpandableNotificationRow;
33 import com.android.systemui.statusbar.TransformableView;
34 import com.android.systemui.statusbar.ViewTransformationHelper;
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 CLIP_CLIPPING_SET = R.id.clip_children_set_tag;
47     private static final int CLIP_CHILDREN_TAG = R.id.clip_children_tag;
48     private static final int CLIP_TO_PADDING = R.id.clip_to_padding_tag;
49     private static final int TRANSFORMATION_START_X = R.id.transformation_start_x_tag;
50     private static final int TRANSFORMATION_START_Y = R.id.transformation_start_y_tag;
51     private static final int TRANSFORMATION_START_SCLALE_X = R.id.transformation_start_scale_x_tag;
52     private static final int TRANSFORMATION_START_SCLALE_Y = R.id.transformation_start_scale_y_tag;
53     private static Pools.SimplePool<TransformState> sInstancePool = new Pools.SimplePool<>(40);
54 
55     protected View mTransformedView;
56     private int[] mOwnPosition = new int[2];
57     private boolean mSameAsAny;
58     private float mTransformationEndY = UNDEFINED;
59     private float mTransformationEndX = UNDEFINED;
60 
initFrom(View view)61     public void initFrom(View view) {
62         mTransformedView = view;
63     }
64 
65     /**
66      * Transforms the {@link #mTransformedView} from the given transformviewstate
67      * @param otherState the state to transform from
68      * @param transformationAmount how much to transform
69      */
transformViewFrom(TransformState otherState, float transformationAmount)70     public void transformViewFrom(TransformState otherState, float transformationAmount) {
71         mTransformedView.animate().cancel();
72         if (sameAs(otherState)) {
73             if (mTransformedView.getVisibility() == View.INVISIBLE
74                     || mTransformedView.getAlpha() != 1.0f) {
75                 // We have the same content, lets show ourselves
76                 mTransformedView.setAlpha(1.0f);
77                 mTransformedView.setVisibility(View.VISIBLE);
78             }
79         } else {
80             CrossFadeHelper.fadeIn(mTransformedView, transformationAmount);
81         }
82         transformViewFullyFrom(otherState, transformationAmount);
83     }
84 
transformViewFullyFrom(TransformState otherState, float transformationAmount)85     public void transformViewFullyFrom(TransformState otherState, float transformationAmount) {
86         transformViewFrom(otherState, TRANSFORM_ALL, null, transformationAmount);
87     }
88 
transformViewFullyFrom(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)89     public void transformViewFullyFrom(TransformState otherState,
90             ViewTransformationHelper.CustomTransformation customTransformation,
91             float transformationAmount) {
92         transformViewFrom(otherState, TRANSFORM_ALL, customTransformation, transformationAmount);
93     }
94 
transformViewVerticalFrom(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)95     public void transformViewVerticalFrom(TransformState otherState,
96             ViewTransformationHelper.CustomTransformation customTransformation,
97             float transformationAmount) {
98         transformViewFrom(otherState, TRANSFORM_Y, customTransformation, transformationAmount);
99     }
100 
transformViewVerticalFrom(TransformState otherState, float transformationAmount)101     public void transformViewVerticalFrom(TransformState otherState, float transformationAmount) {
102         transformViewFrom(otherState, TRANSFORM_Y, null, transformationAmount);
103     }
104 
transformViewFrom(TransformState otherState, int transformationFlags, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)105     private void transformViewFrom(TransformState otherState, int transformationFlags,
106             ViewTransformationHelper.CustomTransformation customTransformation,
107             float transformationAmount) {
108         final View transformedView = mTransformedView;
109         boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
110         boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
111         boolean transformScale = transformScale(otherState);
112         // lets animate the positions correctly
113         if (transformationAmount == 0.0f
114                 || transformX && getTransformationStartX() == UNDEFINED
115                 || transformY && getTransformationStartY() == UNDEFINED
116                 || transformScale && getTransformationStartScaleX() == UNDEFINED
117                 || transformScale && getTransformationStartScaleY() == UNDEFINED) {
118             int[] otherPosition;
119             if (transformationAmount != 0.0f) {
120                 otherPosition = otherState.getLaidOutLocationOnScreen();
121             } else {
122                 otherPosition = otherState.getLocationOnScreen();
123             }
124             int[] ownStablePosition = getLaidOutLocationOnScreen();
125             if (customTransformation == null
126                     || !customTransformation.initTransformation(this, otherState)) {
127                 if (transformX) {
128                     setTransformationStartX(otherPosition[0] - ownStablePosition[0]);
129                 }
130                 if (transformY) {
131                     setTransformationStartY(otherPosition[1] - ownStablePosition[1]);
132                 }
133                 // we also want to animate the scale if we're the same
134                 View otherView = otherState.getTransformedView();
135                 if (transformScale && otherState.getViewWidth() != getViewWidth()) {
136                     setTransformationStartScaleX(otherState.getViewWidth() * otherView.getScaleX()
137                             / (float) getViewWidth());
138                     transformedView.setPivotX(0);
139                 } else {
140                     setTransformationStartScaleX(UNDEFINED);
141                 }
142                 if (transformScale && otherState.getViewHeight() != getViewHeight()) {
143                     setTransformationStartScaleY(otherState.getViewHeight() * otherView.getScaleY()
144                             / (float) getViewHeight());
145                     transformedView.setPivotY(0);
146                 } else {
147                     setTransformationStartScaleY(UNDEFINED);
148                 }
149             }
150             if (!transformX) {
151                 setTransformationStartX(UNDEFINED);
152             }
153             if (!transformY) {
154                 setTransformationStartY(UNDEFINED);
155             }
156             if (!transformScale) {
157                 setTransformationStartScaleX(UNDEFINED);
158                 setTransformationStartScaleY(UNDEFINED);
159             }
160             setClippingDeactivated(transformedView, true);
161         }
162         float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
163                 transformationAmount);
164         if (transformX) {
165             float interpolation = interpolatedValue;
166             if (customTransformation != null) {
167                 Interpolator customInterpolator =
168                         customTransformation.getCustomInterpolator(TRANSFORM_X, true /* isFrom */);
169                 if (customInterpolator != null) {
170                     interpolation = customInterpolator.getInterpolation(transformationAmount);
171                 }
172             }
173             transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
174                     0.0f,
175                     interpolation));
176         }
177         if (transformY) {
178             float interpolation = interpolatedValue;
179             if (customTransformation != null) {
180                 Interpolator customInterpolator =
181                         customTransformation.getCustomInterpolator(TRANSFORM_Y, true /* isFrom */);
182                 if (customInterpolator != null) {
183                     interpolation = customInterpolator.getInterpolation(transformationAmount);
184                 }
185             }
186             transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
187                     0.0f,
188                     interpolation));
189         }
190         if (transformScale) {
191             float transformationStartScaleX = getTransformationStartScaleX();
192             if (transformationStartScaleX != UNDEFINED) {
193                 transformedView.setScaleX(
194                         NotificationUtils.interpolate(transformationStartScaleX,
195                                 1.0f,
196                                 interpolatedValue));
197             }
198             float transformationStartScaleY = getTransformationStartScaleY();
199             if (transformationStartScaleY != UNDEFINED) {
200                 transformedView.setScaleY(
201                         NotificationUtils.interpolate(transformationStartScaleY,
202                                 1.0f,
203                                 interpolatedValue));
204             }
205         }
206     }
207 
getViewWidth()208     protected int getViewWidth() {
209         return mTransformedView.getWidth();
210     }
211 
getViewHeight()212     protected int getViewHeight() {
213         return mTransformedView.getHeight();
214     }
215 
transformScale(TransformState otherState)216     protected boolean transformScale(TransformState otherState) {
217         return false;
218     }
219 
220     /**
221      * Transforms the {@link #mTransformedView} to the given transformviewstate
222      * @param otherState the state to transform from
223      * @param transformationAmount how much to transform
224      * @return whether an animation was started
225      */
transformViewTo(TransformState otherState, float transformationAmount)226     public boolean transformViewTo(TransformState otherState, float transformationAmount) {
227         mTransformedView.animate().cancel();
228         if (sameAs(otherState)) {
229             // We have the same text, lets show ourselfs
230             if (mTransformedView.getVisibility() == View.VISIBLE) {
231                 mTransformedView.setAlpha(0.0f);
232                 mTransformedView.setVisibility(View.INVISIBLE);
233             }
234             return false;
235         } else {
236             CrossFadeHelper.fadeOut(mTransformedView, transformationAmount);
237         }
238         transformViewFullyTo(otherState, transformationAmount);
239         return true;
240     }
241 
transformViewFullyTo(TransformState otherState, float transformationAmount)242     public void transformViewFullyTo(TransformState otherState, float transformationAmount) {
243         transformViewTo(otherState, TRANSFORM_ALL, null, transformationAmount);
244     }
245 
transformViewFullyTo(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)246     public void transformViewFullyTo(TransformState otherState,
247             ViewTransformationHelper.CustomTransformation customTransformation,
248             float transformationAmount) {
249         transformViewTo(otherState, TRANSFORM_ALL, customTransformation, transformationAmount);
250     }
251 
transformViewVerticalTo(TransformState otherState, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)252     public void transformViewVerticalTo(TransformState otherState,
253             ViewTransformationHelper.CustomTransformation customTransformation,
254             float transformationAmount) {
255         transformViewTo(otherState, TRANSFORM_Y, customTransformation, transformationAmount);
256     }
257 
transformViewVerticalTo(TransformState otherState, float transformationAmount)258     public void transformViewVerticalTo(TransformState otherState, float transformationAmount) {
259         transformViewTo(otherState, TRANSFORM_Y, null, transformationAmount);
260     }
261 
transformViewTo(TransformState otherState, int transformationFlags, ViewTransformationHelper.CustomTransformation customTransformation, float transformationAmount)262     private void transformViewTo(TransformState otherState, int transformationFlags,
263             ViewTransformationHelper.CustomTransformation customTransformation,
264             float transformationAmount) {
265         // lets animate the positions correctly
266 
267         final View transformedView = mTransformedView;
268         boolean transformX = (transformationFlags & TRANSFORM_X) != 0;
269         boolean transformY = (transformationFlags & TRANSFORM_Y) != 0;
270         boolean transformScale = transformScale(otherState);
271         // lets animate the positions correctly
272         if (transformationAmount == 0.0f) {
273             if (transformX) {
274                 float transformationStartX = getTransformationStartX();
275                 float start = transformationStartX != UNDEFINED ? transformationStartX
276                         : transformedView.getTranslationX();
277                 setTransformationStartX(start);
278             }
279             if (transformY) {
280                 float transformationStartY = getTransformationStartY();
281                 float start = transformationStartY != UNDEFINED ? transformationStartY
282                         : transformedView.getTranslationY();
283                 setTransformationStartY(start);
284             }
285             View otherView = otherState.getTransformedView();
286             if (transformScale && otherState.getViewWidth() != getViewWidth()) {
287                 setTransformationStartScaleX(transformedView.getScaleX());
288                 transformedView.setPivotX(0);
289             } else {
290                 setTransformationStartScaleX(UNDEFINED);
291             }
292             if (transformScale && otherState.getViewHeight() != getViewHeight()) {
293                 setTransformationStartScaleY(transformedView.getScaleY());
294                 transformedView.setPivotY(0);
295             } else {
296                 setTransformationStartScaleY(UNDEFINED);
297             }
298             setClippingDeactivated(transformedView, true);
299         }
300         float interpolatedValue = Interpolators.FAST_OUT_SLOW_IN.getInterpolation(
301                 transformationAmount);
302         int[] otherStablePosition = otherState.getLaidOutLocationOnScreen();
303         int[] ownPosition = getLaidOutLocationOnScreen();
304         if (transformX) {
305             float endX = otherStablePosition[0] - ownPosition[0];
306             float interpolation = interpolatedValue;
307             if (customTransformation != null) {
308                 if (customTransformation.customTransformTarget(this, otherState)) {
309                     endX = mTransformationEndX;
310                 }
311                 Interpolator customInterpolator =
312                         customTransformation.getCustomInterpolator(TRANSFORM_X, false /* isFrom */);
313                 if (customInterpolator != null) {
314                     interpolation = customInterpolator.getInterpolation(transformationAmount);
315                 }
316             }
317             transformedView.setTranslationX(NotificationUtils.interpolate(getTransformationStartX(),
318                     endX,
319                     interpolation));
320         }
321         if (transformY) {
322             float endY = otherStablePosition[1] - ownPosition[1];
323             float interpolation = interpolatedValue;
324             if (customTransformation != null) {
325                 if (customTransformation.customTransformTarget(this, otherState)) {
326                     endY = mTransformationEndY;
327                 }
328                 Interpolator customInterpolator =
329                         customTransformation.getCustomInterpolator(TRANSFORM_Y, false /* isFrom */);
330                 if (customInterpolator != null) {
331                     interpolation = customInterpolator.getInterpolation(transformationAmount);
332                 }
333             }
334             transformedView.setTranslationY(NotificationUtils.interpolate(getTransformationStartY(),
335                     endY,
336                     interpolation));
337         }
338         if (transformScale) {
339             View otherView = otherState.getTransformedView();
340             float transformationStartScaleX = getTransformationStartScaleX();
341             if (transformationStartScaleX != UNDEFINED) {
342                 transformedView.setScaleX(
343                         NotificationUtils.interpolate(transformationStartScaleX,
344                                 (otherState.getViewWidth() / (float) getViewWidth()),
345                                 interpolatedValue));
346             }
347             float transformationStartScaleY = getTransformationStartScaleY();
348             if (transformationStartScaleY != UNDEFINED) {
349                 transformedView.setScaleY(
350                         NotificationUtils.interpolate(transformationStartScaleY,
351                                 (otherState.getViewHeight() / (float) getViewHeight()),
352                                 interpolatedValue));
353             }
354         }
355     }
356 
setClippingDeactivated(final View transformedView, boolean deactivated)357     public static void setClippingDeactivated(final View transformedView, boolean deactivated) {
358         if (!(transformedView.getParent() instanceof ViewGroup)) {
359             return;
360         }
361         ViewGroup view = (ViewGroup) transformedView.getParent();
362         while (true) {
363             ArraySet<View> clipSet = (ArraySet<View>) view.getTag(CLIP_CLIPPING_SET);
364             if (clipSet == null) {
365                 clipSet = new ArraySet<>();
366                 view.setTag(CLIP_CLIPPING_SET, clipSet);
367             }
368             Boolean clipChildren = (Boolean) view.getTag(CLIP_CHILDREN_TAG);
369             if (clipChildren == null) {
370                 clipChildren = view.getClipChildren();
371                 view.setTag(CLIP_CHILDREN_TAG, clipChildren);
372             }
373             Boolean clipToPadding = (Boolean) view.getTag(CLIP_TO_PADDING);
374             if (clipToPadding == null) {
375                 clipToPadding = view.getClipToPadding();
376                 view.setTag(CLIP_TO_PADDING, clipToPadding);
377             }
378             ExpandableNotificationRow row = view instanceof ExpandableNotificationRow
379                     ? (ExpandableNotificationRow) view
380                     : null;
381             if (!deactivated) {
382                 clipSet.remove(transformedView);
383                 if (clipSet.isEmpty()) {
384                     view.setClipChildren(clipChildren);
385                     view.setClipToPadding(clipToPadding);
386                     view.setTag(CLIP_CLIPPING_SET, null);
387                     if (row != null) {
388                         row.setClipToActualHeight(true);
389                     }
390                 }
391             } else {
392                 clipSet.add(transformedView);
393                 view.setClipChildren(false);
394                 view.setClipToPadding(false);
395                 if (row != null && row.isChildInGroup()) {
396                     // We still want to clip to the parent's height
397                     row.setClipToActualHeight(false);
398                 }
399             }
400             if (row != null && !row.isChildInGroup()) {
401                 return;
402             }
403             final ViewParent parent = view.getParent();
404             if (parent instanceof ViewGroup) {
405                 view = (ViewGroup) parent;
406             } else {
407                 return;
408             }
409         }
410     }
411 
getLaidOutLocationOnScreen()412     public int[] getLaidOutLocationOnScreen() {
413         int[] location = getLocationOnScreen();
414         // remove translation
415         location[0] -= mTransformedView.getTranslationX();
416         location[1] -= mTransformedView.getTranslationY();
417         return location;
418     }
419 
getLocationOnScreen()420     public int[] getLocationOnScreen() {
421         mTransformedView.getLocationOnScreen(mOwnPosition);
422 
423         // remove scale
424         mOwnPosition[0] -= (1.0f - mTransformedView.getScaleX()) * mTransformedView.getPivotX();
425         mOwnPosition[1] -= (1.0f - mTransformedView.getScaleY()) * mTransformedView.getPivotY();
426         return mOwnPosition;
427     }
428 
sameAs(TransformState otherState)429     protected boolean sameAs(TransformState otherState) {
430         return mSameAsAny;
431     }
432 
appear(float transformationAmount, TransformableView otherView)433     public void appear(float transformationAmount, TransformableView otherView) {
434         // There's no other view, lets fade us in
435         // Certain views need to prepare the fade in and make sure its children are
436         // completely visible. An example is the notification header.
437         if (transformationAmount == 0.0f) {
438             prepareFadeIn();
439         }
440         CrossFadeHelper.fadeIn(mTransformedView, transformationAmount);
441     }
442 
disappear(float transformationAmount, TransformableView otherView)443     public void disappear(float transformationAmount, TransformableView otherView) {
444         CrossFadeHelper.fadeOut(mTransformedView, transformationAmount);
445     }
446 
createFrom(View view)447     public static TransformState createFrom(View view) {
448         if (view instanceof TextView) {
449             TextViewTransformState result = TextViewTransformState.obtain();
450             result.initFrom(view);
451             return result;
452         }
453         if (view.getId() == com.android.internal.R.id.actions_container) {
454             ActionListTransformState result = ActionListTransformState.obtain();
455             result.initFrom(view);
456             return result;
457         }
458         if (view instanceof ImageView) {
459             ImageTransformState result = ImageTransformState.obtain();
460             result.initFrom(view);
461             if (view.getId() == com.android.internal.R.id.reply_icon_action) {
462                 ((TransformState) result).setIsSameAsAnyView(true);
463             }
464             return result;
465         }
466         if (view instanceof ProgressBar) {
467             ProgressTransformState result = ProgressTransformState.obtain();
468             result.initFrom(view);
469             return result;
470         }
471         TransformState result = obtain();
472         result.initFrom(view);
473         return result;
474     }
475 
setIsSameAsAnyView(boolean sameAsAny)476     private void setIsSameAsAnyView(boolean sameAsAny) {
477         mSameAsAny = sameAsAny;
478     }
479 
recycle()480     public void recycle() {
481         reset();
482         if (getClass() == TransformState.class) {
483             sInstancePool.release(this);
484         }
485     }
486 
setTransformationEndY(float transformationEndY)487     public void setTransformationEndY(float transformationEndY) {
488         mTransformationEndY = transformationEndY;
489     }
490 
setTransformationEndX(float transformationEndX)491     public void setTransformationEndX(float transformationEndX) {
492         mTransformationEndX = transformationEndX;
493     }
494 
getTransformationStartX()495     public float getTransformationStartX() {
496         Object tag = mTransformedView.getTag(TRANSFORMATION_START_X);
497         return tag == null ? UNDEFINED : (float) tag;
498     }
499 
getTransformationStartY()500     public float getTransformationStartY() {
501         Object tag = mTransformedView.getTag(TRANSFORMATION_START_Y);
502         return tag == null ? UNDEFINED : (float) tag;
503     }
504 
getTransformationStartScaleX()505     public float getTransformationStartScaleX() {
506         Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_X);
507         return tag == null ? UNDEFINED : (float) tag;
508     }
509 
getTransformationStartScaleY()510     public float getTransformationStartScaleY() {
511         Object tag = mTransformedView.getTag(TRANSFORMATION_START_SCLALE_Y);
512         return tag == null ? UNDEFINED : (float) tag;
513     }
514 
setTransformationStartX(float transformationStartX)515     public void setTransformationStartX(float transformationStartX) {
516         mTransformedView.setTag(TRANSFORMATION_START_X, transformationStartX);
517     }
518 
setTransformationStartY(float transformationStartY)519     public void setTransformationStartY(float transformationStartY) {
520         mTransformedView.setTag(TRANSFORMATION_START_Y, transformationStartY);
521     }
522 
setTransformationStartScaleX(float startScaleX)523     private void setTransformationStartScaleX(float startScaleX) {
524         mTransformedView.setTag(TRANSFORMATION_START_SCLALE_X, startScaleX);
525     }
526 
setTransformationStartScaleY(float startScaleY)527     private void setTransformationStartScaleY(float startScaleY) {
528         mTransformedView.setTag(TRANSFORMATION_START_SCLALE_Y, startScaleY);
529     }
530 
reset()531     protected void reset() {
532         mTransformedView = null;
533         mSameAsAny = false;
534         mTransformationEndX = UNDEFINED;
535         mTransformationEndY = UNDEFINED;
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 }
582