• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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 android.support.v17.preference;
18 
19 import android.animation.Animator;
20 import android.animation.AnimatorListenerAdapter;
21 import android.animation.AnimatorSet;
22 import android.animation.ObjectAnimator;
23 import android.animation.TimeInterpolator;
24 import android.app.Fragment;
25 import android.graphics.Path;
26 import android.transition.Fade;
27 import android.transition.Transition;
28 import android.transition.TransitionValues;
29 import android.transition.Visibility;
30 import android.view.Gravity;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.view.animation.DecelerateInterpolator;
34 
35 /**
36  * @hide
37  */
38 public class LeanbackPreferenceFragmentTransitionHelperApi21 {
39 
addTransitions(Fragment f)40     public static void addTransitions(Fragment f) {
41         final Transition transitionStartEdge = new FadeAndShortSlideTransition(Gravity.START);
42         final Transition transitionEndEdge = new FadeAndShortSlideTransition(Gravity.END);
43 
44         f.setEnterTransition(transitionEndEdge);
45         f.setExitTransition(transitionStartEdge);
46         f.setReenterTransition(transitionStartEdge);
47         f.setReturnTransition(transitionEndEdge);
48     }
49 
50     private static class FadeAndShortSlideTransition extends Visibility {
51 
52         private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
53 //        private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
54         private static final String PROPNAME_SCREEN_POSITION =
55                 "android:fadeAndShortSlideTransition:screenPosition";
56 
57         private CalculateSlide mSlideCalculator = sCalculateEnd;
58         private Visibility mFade = new Fade();
59 
60         private interface CalculateSlide {
61 
62             /** Returns the translation value for view when it goes out of the scene */
getGoneX(ViewGroup sceneRoot, View view)63             float getGoneX(ViewGroup sceneRoot, View view);
64         }
65 
66         private static final CalculateSlide sCalculateStart = new CalculateSlide() {
67             @Override
68             public float getGoneX(ViewGroup sceneRoot, View view) {
69                 final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
70                 final float x;
71                 if (isRtl) {
72                     x = view.getTranslationX() + sceneRoot.getWidth() / 4;
73                 } else {
74                     x = view.getTranslationX() - sceneRoot.getWidth() / 4;
75                 }
76                 return x;
77             }
78         };
79 
80         private static final CalculateSlide sCalculateEnd = new CalculateSlide() {
81             @Override
82             public float getGoneX(ViewGroup sceneRoot, View view) {
83                 final boolean isRtl = sceneRoot.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
84                 final float x;
85                 if (isRtl) {
86                     x = view.getTranslationX() - sceneRoot.getWidth() / 4;
87                 } else {
88                     x = view.getTranslationX() + sceneRoot.getWidth() / 4;
89                 }
90                 return x;
91             }
92         };
93 
FadeAndShortSlideTransition(int slideEdge)94         public FadeAndShortSlideTransition(int slideEdge) {
95             setSlideEdge(slideEdge);
96         }
97 
98         @Override
setEpicenterCallback(EpicenterCallback epicenterCallback)99         public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
100             super.setEpicenterCallback(epicenterCallback);
101             mFade.setEpicenterCallback(epicenterCallback);
102         }
103 
captureValues(TransitionValues transitionValues)104         private void captureValues(TransitionValues transitionValues) {
105             View view = transitionValues.view;
106             int[] position = new int[2];
107             view.getLocationOnScreen(position);
108             transitionValues.values.put(PROPNAME_SCREEN_POSITION, position[0]);
109         }
110 
111         @Override
captureStartValues(TransitionValues transitionValues)112         public void captureStartValues(TransitionValues transitionValues) {
113             super.captureStartValues(transitionValues);
114             mFade.captureStartValues(transitionValues);
115             captureValues(transitionValues);
116         }
117 
118         @Override
captureEndValues(TransitionValues transitionValues)119         public void captureEndValues(TransitionValues transitionValues) {
120             super.captureEndValues(transitionValues);
121             mFade.captureEndValues(transitionValues);
122             captureValues(transitionValues);
123         }
124 
setSlideEdge(int slideEdge)125         public void setSlideEdge(int slideEdge) {
126             switch (slideEdge) {
127                 case Gravity.START:
128                     mSlideCalculator = sCalculateStart;
129                     break;
130                 case Gravity.END:
131                     mSlideCalculator = sCalculateEnd;
132                     break;
133                 default:
134                     throw new IllegalArgumentException("Invalid slide direction");
135             }
136 //            SidePropagation propagation = new SidePropagation();
137 //            propagation.setSide(slideEdge);
138 //            setPropagation(propagation);
139         }
140 
141         @Override
onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)142         public Animator onAppear(ViewGroup sceneRoot, View view,
143                 TransitionValues startValues, TransitionValues endValues) {
144             if (endValues == null) {
145                 return null;
146             }
147             Integer position = (Integer) endValues.values.get(PROPNAME_SCREEN_POSITION);
148             float endX = view.getTranslationX();
149             float startX = mSlideCalculator.getGoneX(sceneRoot, view);
150             final Animator slideAnimator = TranslationAnimationCreator
151                     .createAnimation(view, endValues, position,
152                             startX, endX, sDecelerate, this);
153             final AnimatorSet set = new AnimatorSet();
154             set.play(slideAnimator)
155                     .with(mFade.onAppear(sceneRoot, view, startValues, endValues));
156 
157             return set;
158         }
159 
160         @Override
onDisappear(ViewGroup sceneRoot, View view, TransitionValues startValues, TransitionValues endValues)161         public Animator onDisappear(ViewGroup sceneRoot, View view,
162                 TransitionValues startValues, TransitionValues endValues) {
163             if (startValues == null) {
164                 return null;
165             }
166             Integer position = (Integer) startValues.values.get(PROPNAME_SCREEN_POSITION);
167             float startX = view.getTranslationX();
168             float endX = mSlideCalculator.getGoneX(sceneRoot, view);
169             final Animator slideAnimator = TranslationAnimationCreator
170                     .createAnimation(view, startValues, position,
171                             startX, endX, sDecelerate /*sAccelerate*/, this);
172             final AnimatorSet set = new AnimatorSet();
173             set.play(slideAnimator)
174                     .with(mFade.onDisappear(sceneRoot, view, startValues, endValues));
175 
176             return set;
177         }
178 
179         @Override
addListener(TransitionListener listener)180         public Transition addListener(TransitionListener listener) {
181             mFade.addListener(listener);
182             return super.addListener(listener);
183         }
184 
185         @Override
removeListener(TransitionListener listener)186         public Transition removeListener(TransitionListener listener) {
187             mFade.removeListener(listener);
188             return super.removeListener(listener);
189         }
190 
191         @Override
clone()192         public Transition clone() {
193             FadeAndShortSlideTransition clone = null;
194             clone = (FadeAndShortSlideTransition) super.clone();
195             clone.mFade = (Visibility) mFade.clone();
196             return clone;
197         }
198     }
199 
200     /**
201      * This class is used by Slide and Explode to create an animator that goes from the start
202      * position to the end position. It takes into account the canceled position so that it
203      * will not blink out or shift suddenly when the transition is interrupted.
204      */
205     private static class TranslationAnimationCreator {
206 
207         /**
208          * Creates an animator that can be used for x and/or y translations. When interrupted,
209          * it sets a tag to keep track of the position so that it may be continued from position.
210          *
211          * @param view The view being moved. This may be in the overlay for onDisappear.
212          * @param values The values containing the view in the view hierarchy.
213          * @param viewPosX The x screen coordinate of view
214          * @param startX The start translation x of view
215          * @param endX The end translation x of view
216          * @param interpolator The interpolator to use with this animator.
217          * @return An animator that moves from (startX, startY) to (endX, endY) unless there was
218          * a previous interruption, in which case it moves from the current position to
219          * (endX, endY).
220          */
createAnimation(View view, TransitionValues values, int viewPosX, float startX, float endX, TimeInterpolator interpolator, Transition transition)221         static Animator createAnimation(View view, TransitionValues values, int viewPosX,
222                 float startX, float endX, TimeInterpolator interpolator,
223                 Transition transition) {
224             float terminalX = view.getTranslationX();
225             Integer startPosition = (Integer) values.view.getTag(R.id.transitionPosition);
226             if (startPosition != null) {
227                 startX = startPosition - viewPosX + terminalX;
228             }
229             // Initial position is at translation startX, startY, so position is offset by that
230             // amount
231             int startPosX = viewPosX + Math.round(startX - terminalX);
232 
233             view.setTranslationX(startX);
234             if (startX == endX) {
235                 return null;
236             }
237             Path path = new Path();
238             path.moveTo(startX, 0);
239             path.lineTo(endX, 0);
240             ObjectAnimator anim =
241                     ObjectAnimator.ofFloat(view, View.TRANSLATION_X, View.TRANSLATION_Y, path);
242 
243             TransitionPositionListener listener = new TransitionPositionListener(view, values.view,
244                     startPosX, terminalX);
245             transition.addListener(listener);
246             anim.addListener(listener);
247             anim.addPauseListener(listener);
248             anim.setInterpolator(interpolator);
249             return anim;
250         }
251 
252         private static class TransitionPositionListener extends AnimatorListenerAdapter implements
253                 Transition.TransitionListener {
254 
255             private final View mViewInHierarchy;
256             private final View mMovingView;
257             private final int mStartX;
258             private Integer mTransitionPosition;
259             private float mPausedX;
260             private final float mTerminalX;
261 
TransitionPositionListener(View movingView, View viewInHierarchy, int startX, float terminalX)262             private TransitionPositionListener(View movingView, View viewInHierarchy,
263                     int startX, float terminalX) {
264                 mMovingView = movingView;
265                 mViewInHierarchy = viewInHierarchy;
266                 mStartX = startX - Math.round(mMovingView.getTranslationX());
267                 mTerminalX = terminalX;
268                 mTransitionPosition = (Integer) mViewInHierarchy.getTag(R.id.transitionPosition);
269                 if (mTransitionPosition != null) {
270                     mViewInHierarchy.setTag(R.id.transitionPosition, null);
271                 }
272             }
273 
274             @Override
onAnimationCancel(Animator animation)275             public void onAnimationCancel(Animator animation) {
276                 mTransitionPosition = Math.round(mStartX + mMovingView.getTranslationX());
277                 mViewInHierarchy.setTag(R.id.transitionPosition, mTransitionPosition);
278             }
279 
280             @Override
onAnimationEnd(Animator animator)281             public void onAnimationEnd(Animator animator) {
282             }
283 
284             @Override
onAnimationPause(Animator animator)285             public void onAnimationPause(Animator animator) {
286                 mPausedX = mMovingView.getTranslationX();
287                 mMovingView.setTranslationX(mTerminalX);
288             }
289 
290             @Override
onAnimationResume(Animator animator)291             public void onAnimationResume(Animator animator) {
292                 mMovingView.setTranslationX(mPausedX);
293             }
294 
295             @Override
onTransitionStart(Transition transition)296             public void onTransitionStart(Transition transition) {
297             }
298 
299             @Override
onTransitionEnd(Transition transition)300             public void onTransitionEnd(Transition transition) {
301                 mMovingView.setTranslationX(mTerminalX);
302             }
303 
304             @Override
onTransitionCancel(Transition transition)305             public void onTransitionCancel(Transition transition) {
306             }
307 
308             @Override
onTransitionPause(Transition transition)309             public void onTransitionPause(Transition transition) {
310             }
311 
312             @Override
onTransitionResume(Transition transition)313             public void onTransitionResume(Transition transition) {
314             }
315         }
316 
317     }
318 
319 }
320