1 package androidx.leanback.transition;
2 
3 import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP_PREFIX;
4 
5 import android.animation.Animator;
6 import android.animation.AnimatorListenerAdapter;
7 import android.animation.ObjectAnimator;
8 import android.animation.TimeInterpolator;
9 import android.graphics.Path;
10 import android.transition.Transition;
11 import android.transition.Transition.TransitionListener;
12 import android.transition.TransitionValues;
13 import android.view.View;
14 
15 import androidx.annotation.RequiresApi;
16 import androidx.annotation.RestrictTo;
17 import androidx.leanback.R;
18 
19 /**
20  * This class is used by Slide and Explode to create an animator that goes from the start
21  * position to the end position. It takes into account the canceled position so that it
22  * will not blink out or shift suddenly when the transition is interrupted.
23  */
24 @RequiresApi(21)
25 @RestrictTo(LIBRARY_GROUP_PREFIX)
26 class TranslationAnimationCreator {
27 
28     /**
29      * Creates an animator that can be used for x and/or y translations. When interrupted,
30      * it sets a tag to keep track of the position so that it may be continued from position.
31      *
32      * @param view The view being moved. This may be in the overlay for onDisappear.
33      * @param values The values containing the view in the view hierarchy.
34      * @param viewPosX The x screen coordinate of view
35      * @param viewPosY The y screen coordinate of view
36      * @param startX The start translation x of view
37      * @param startY The start translation y of view
38      * @param endX The end translation x of view
39      * @param endY The end translation y of view
40      * @param interpolator The interpolator to use with this animator.
41      * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
42      * a previous interruption, in which case it moves from the current position to (endX, endY).
43      */
createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY, float startX, float startY, float endX, float endY, TimeInterpolator interpolator, Transition transition)44     static Animator createAnimation(View view, TransitionValues values, int viewPosX, int viewPosY,
45             float startX, float startY, float endX, float endY, TimeInterpolator interpolator,
46             Transition transition) {
47         float terminalX = view.getTranslationX();
48         float terminalY = view.getTranslationY();
49         int[] startPosition = (int[]) values.view.getTag(R.id.transitionPosition);
50         if (startPosition != null) {
51             startX = startPosition[0] - viewPosX + terminalX;
52             startY = startPosition[1] - viewPosY + terminalY;
53         }
54         // Initial position is at translation startX, startY, so position is offset by that amount
55         int startPosX = viewPosX + Math.round(startX - terminalX);
56         int startPosY = viewPosY + Math.round(startY - terminalY);
57 
58         view.setTranslationX(startX);
59         view.setTranslationY(startY);
60         if (startX == endX && startY == endY) {
61             return null;
62         }
63         Path path = new Path();
64         path.moveTo(startX, startY);
65         path.lineTo(endX, endY);
66         ObjectAnimator anim = ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y,
67                 path);
68 
69         TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
70                 startPosX, startPosY, terminalX, terminalY);
71         transition.addListener(listener);
72         anim.addListener(listener);
73         anim.addPauseListener(listener);
74         anim.setInterpolator(interpolator);
75         return anim;
76     }
77 
78     private static class TransitionPositionListener extends AnimatorListenerAdapter implements
79             TransitionListener {
80 
81         private final View mViewInHierarchy;
82         private final View mMovingView;
83         private final int mStartX;
84         private final int mStartY;
85         private int[] mTransitionPosition;
86         private float mPausedX;
87         private float mPausedY;
88         private final float mTerminalX;
89         private final float mTerminalY;
90 
TransitionPositionListener(View movingView, View viewInHierarchy, int startX, int startY, float terminalX, float terminalY)91         TransitionPositionListener(View movingView, View viewInHierarchy,
92                 int startX, int startY, float terminalX, float terminalY) {
93             mMovingView = movingView;
94             mViewInHierarchy = viewInHierarchy;
95             mStartX = startX - Math.round(mMovingView.getTranslationX());
96             mStartY = startY - Math.round(mMovingView.getTranslationY());
97             mTerminalX = terminalX;
98             mTerminalY = terminalY;
99             mTransitionPosition = (int[]) mViewInHierarchy.getTag(R.id.transitionPosition);
100             if (mTransitionPosition != null) {
101                 mViewInHierarchy.setTag(R.id.transitionPosition, null);
102             }
103         }
104 
105         @Override
onAnimationCancel(Animator animation)106         public void onAnimationCancel(Animator animation) {
107             if (mTransitionPosition == null) {
108                 mTransitionPosition = new int[2];
109             }
110             mTransitionPosition[0] = Math.round(mStartX + mMovingView.getTranslationX());
111             mTransitionPosition[1] = Math.round(mStartY + mMovingView.getTranslationY());
112             mViewInHierarchy.setTag(R.id.transitionPosition, mTransitionPosition);
113         }
114 
115         @Override
onAnimationEnd(Animator animator)116         public void onAnimationEnd(Animator animator) {
117         }
118 
119         @Override
onAnimationPause(Animator animator)120         public void onAnimationPause(Animator animator) {
121             mPausedX = mMovingView.getTranslationX();
122             mPausedY = mMovingView.getTranslationY();
123             mMovingView.setTranslationX(mTerminalX);
124             mMovingView.setTranslationY(mTerminalY);
125         }
126 
127         @Override
onAnimationResume(Animator animator)128         public void onAnimationResume(Animator animator) {
129             mMovingView.setTranslationX(mPausedX);
130             mMovingView.setTranslationY(mPausedY);
131         }
132 
133         @Override
onTransitionStart(Transition transition)134         public void onTransitionStart(Transition transition) {
135         }
136 
137         @Override
onTransitionEnd(Transition transition)138         public void onTransitionEnd(Transition transition) {
139             mMovingView.setTranslationX(mTerminalX);
140             mMovingView.setTranslationY(mTerminalY);
141         }
142 
143         @Override
onTransitionCancel(Transition transition)144         public void onTransitionCancel(Transition transition) {
145         }
146 
147         @Override
onTransitionPause(Transition transition)148         public void onTransitionPause(Transition transition) {
149         }
150 
151         @Override
onTransitionResume(Transition transition)152         public void onTransitionResume(Transition transition) {
153         }
154     }
155 
TranslationAnimationCreator()156     private TranslationAnimationCreator() {
157     }
158 }
159