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.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