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