• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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 package android.support.v17.leanback.transition;
17 
18 import android.animation.Animator;
19 import android.animation.AnimatorListenerAdapter;
20 import android.animation.AnimatorSet;
21 import android.animation.ObjectAnimator;
22 import android.animation.TimeInterpolator;
23 import android.animation.ValueAnimator;
24 import android.content.Context;
25 import android.content.res.TypedArray;
26 import android.graphics.Rect;
27 import android.util.AttributeSet;
28 import android.util.Log;
29 import android.util.Property;
30 import android.view.Gravity;
31 import android.view.View;
32 import android.view.ViewGroup;
33 import android.view.animation.AccelerateInterpolator;
34 import android.view.animation.AnimationUtils;
35 import android.view.animation.DecelerateInterpolator;
36 import android.transition.Visibility;
37 import android.transition.Transition;
38 import android.transition.TransitionValues;
39 import android.support.v17.leanback.R;
40 
41 /**
42  * Slide distance toward/from a edge.
43  * This is a limited Slide implementation for KitKat without propagation support.
44  * @hide
45  */
46 class SlideKitkat extends Visibility {
47     private static final String TAG = "SlideKitkat";
48 
49     private static final TimeInterpolator sDecelerate = new DecelerateInterpolator();
50     private static final TimeInterpolator sAccelerate = new AccelerateInterpolator();
51 
52     private int mSlideEdge;
53     private CalculateSlide mSlideCalculator;
54 
55     private interface CalculateSlide {
56         /** Returns the translation value for view when it out of the scene */
getGone(View view)57         float getGone(View view);
58 
59         /** Returns the translation value for view when it is in the scene */
getHere(View view)60         float getHere(View view);
61 
62         /** Returns the property to animate translation */
getProperty()63         Property<View, Float> getProperty();
64     }
65 
66     private static abstract class CalculateSlideHorizontal implements CalculateSlide {
67         @Override
getHere(View view)68         public float getHere(View view) {
69             return view.getTranslationX();
70         }
71 
72         @Override
getProperty()73         public Property<View, Float> getProperty() {
74             return View.TRANSLATION_X;
75         }
76     }
77 
78     private static abstract class CalculateSlideVertical implements CalculateSlide {
79         @Override
getHere(View view)80         public float getHere(View view) {
81             return view.getTranslationY();
82         }
83 
84         @Override
getProperty()85         public Property<View, Float> getProperty() {
86             return View.TRANSLATION_Y;
87         }
88     }
89 
90     private static final CalculateSlide sCalculateLeft = new CalculateSlideHorizontal() {
91         @Override
92         public float getGone(View view) {
93             return view.getTranslationX() - view.getWidth();
94         }
95     };
96 
97     private static final CalculateSlide sCalculateTop = new CalculateSlideVertical() {
98         @Override
99         public float getGone(View view) {
100             return view.getTranslationY() - view.getHeight();
101         }
102     };
103 
104     private static final CalculateSlide sCalculateRight = new CalculateSlideHorizontal() {
105         @Override
106         public float getGone(View view) {
107             return view.getTranslationX() + view.getWidth();
108         }
109     };
110 
111     private static final CalculateSlide sCalculateBottom = new CalculateSlideVertical() {
112         @Override
113         public float getGone(View view) {
114             return view.getTranslationY() + view.getHeight();
115         }
116     };
117 
118     private static final CalculateSlide sCalculateStart = new CalculateSlideHorizontal() {
119         @Override
120         public float getGone(View view) {
121             if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
122                 return view.getTranslationX() + view.getWidth();
123             } else {
124                 return view.getTranslationX() - view.getWidth();
125             }
126         }
127     };
128 
129     private static final CalculateSlide sCalculateEnd = new CalculateSlideHorizontal() {
130         @Override
131         public float getGone(View view) {
132             if (view.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
133                 return view.getTranslationX() - view.getWidth();
134             } else {
135                 return view.getTranslationX() + view.getWidth();
136             }
137         }
138     };
139 
SlideKitkat()140     public SlideKitkat() {
141         setSlideEdge(Gravity.BOTTOM);
142     }
143 
SlideKitkat(Context context, AttributeSet attrs)144     public SlideKitkat(Context context, AttributeSet attrs) {
145         TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.lbSlide);
146         int edge = a.getInt(R.styleable.lbSlide_lb_slideEdge, Gravity.BOTTOM);
147         setSlideEdge(edge);
148         long duration = a.getInt(R.styleable.lbSlide_android_duration, -1);
149         if (duration >= 0) {
150             setDuration(duration);
151         }
152         long startDelay = a.getInt(R.styleable.lbSlide_android_startDelay, -1);
153         if (startDelay > 0) {
154             setStartDelay(startDelay);
155         }
156         final int resID = a.getResourceId(R.styleable.lbSlide_android_interpolator, 0);
157         if (resID > 0) {
158             setInterpolator(AnimationUtils.loadInterpolator(context, resID));
159         }
160         a.recycle();
161     }
162 
163     /**
164      * Change the edge that Views appear and disappear from.
165      *
166      * @param slideEdge The edge of the scene to use for Views appearing and disappearing. One of
167      *                  {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
168      *                  {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
169      *                  {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
170      */
setSlideEdge(int slideEdge)171     public void setSlideEdge(int slideEdge) {
172         switch (slideEdge) {
173             case Gravity.LEFT:
174                 mSlideCalculator = sCalculateLeft;
175                 break;
176             case Gravity.TOP:
177                 mSlideCalculator = sCalculateTop;
178                 break;
179             case Gravity.RIGHT:
180                 mSlideCalculator = sCalculateRight;
181                 break;
182             case Gravity.BOTTOM:
183                 mSlideCalculator = sCalculateBottom;
184                 break;
185             case Gravity.START:
186                 mSlideCalculator = sCalculateStart;
187                 break;
188             case Gravity.END:
189                 mSlideCalculator = sCalculateEnd;
190                 break;
191             default:
192                 throw new IllegalArgumentException("Invalid slide direction");
193         }
194         mSlideEdge = slideEdge;
195     }
196 
197     /**
198      * Returns the edge that Views appear and disappear from.
199      * @return the edge of the scene to use for Views appearing and disappearing. One of
200      *         {@link android.view.Gravity#LEFT}, {@link android.view.Gravity#TOP},
201      *         {@link android.view.Gravity#RIGHT}, {@link android.view.Gravity#BOTTOM},
202      *         {@link android.view.Gravity#START}, {@link android.view.Gravity#END}.
203      */
getSlideEdge()204     public int getSlideEdge() {
205         return mSlideEdge;
206     }
207 
createAnimation(final View view, Property<View, Float> property, float start, float end, float terminalValue, TimeInterpolator interpolator, int finalVisibility)208     private Animator createAnimation(final View view, Property<View, Float> property,
209             float start, float end, float terminalValue, TimeInterpolator interpolator,
210             int finalVisibility) {
211         float[] startPosition = (float[]) view.getTag(R.id.lb_slide_transition_value);
212         if (startPosition != null) {
213             start = View.TRANSLATION_Y == property ? startPosition[1] : startPosition[0];
214             view.setTag(R.id.lb_slide_transition_value, null);
215         }
216         final ObjectAnimator anim = ObjectAnimator.ofFloat(view, property, start, end);
217 
218         SlideAnimatorListener listener = new SlideAnimatorListener(view, property, terminalValue, end,
219                 finalVisibility);
220         anim.addListener(listener);
221         anim.addPauseListener(listener);
222         anim.setInterpolator(interpolator);
223         return anim;
224     }
225 
226     @Override
onAppear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility)227     public Animator onAppear(ViewGroup sceneRoot,
228             TransitionValues startValues, int startVisibility,
229             TransitionValues endValues, int endVisibility) {
230         View view = (endValues != null) ? endValues.view : null;
231         if (view == null) {
232             return null;
233         }
234         float end = mSlideCalculator.getHere(view);
235         float start = mSlideCalculator.getGone(view);
236         return createAnimation(view, mSlideCalculator.getProperty(), start, end, end, sDecelerate,
237                 View.VISIBLE);
238     }
239 
240     @Override
onDisappear(ViewGroup sceneRoot, TransitionValues startValues, int startVisibility, TransitionValues endValues, int endVisibility)241     public Animator onDisappear(ViewGroup sceneRoot,
242             TransitionValues startValues, int startVisibility,
243             TransitionValues endValues, int endVisibility) {
244         View view = (startValues != null) ? startValues.view : null;
245         if (view == null) {
246             return null;
247         }
248         float start = mSlideCalculator.getHere(view);
249         float end = mSlideCalculator.getGone(view);
250 
251         return createAnimation(view, mSlideCalculator.getProperty(), start, end, start,
252                 sAccelerate, View.INVISIBLE);
253     }
254 
255     private static class SlideAnimatorListener extends AnimatorListenerAdapter {
256         private boolean mCanceled = false;
257         private float mPausedValue;
258         private final View mView;
259         private final float mEndValue;
260         private final float mTerminalValue;
261         private final int mFinalVisibility;
262         private final Property<View, Float> mProp;
263 
SlideAnimatorListener(View view, Property<View, Float> prop, float terminalValue, float endValue, int finalVisibility)264         public SlideAnimatorListener(View view, Property<View, Float> prop,
265                 float terminalValue, float endValue, int finalVisibility) {
266             mProp = prop;
267             mView = view;
268             mTerminalValue = terminalValue;
269             mEndValue = endValue;
270             mFinalVisibility = finalVisibility;
271             view.setVisibility(View.VISIBLE);
272         }
273 
274         @Override
onAnimationCancel(Animator animator)275         public void onAnimationCancel(Animator animator) {
276             float[] transitionPosition = new float[2];
277             transitionPosition[0] = mView.getTranslationX();
278             transitionPosition[1] = mView.getTranslationY();
279             mView.setTag(R.id.lb_slide_transition_value, transitionPosition);
280             mProp.set(mView, mTerminalValue);
281             mCanceled = true;
282         }
283 
284         @Override
onAnimationEnd(Animator animator)285         public void onAnimationEnd(Animator animator) {
286             if (!mCanceled) {
287                 mProp.set(mView, mTerminalValue);
288             }
289             mView.setVisibility(mFinalVisibility);
290         }
291 
292         @Override
onAnimationPause(Animator animator)293         public void onAnimationPause(Animator animator) {
294             mPausedValue = mProp.get(mView);
295             mProp.set(mView, mEndValue);
296             mView.setVisibility(mFinalVisibility);
297         }
298 
299         @Override
onAnimationResume(Animator animator)300         public void onAnimationResume(Animator animator) {
301             mProp.set(mView, mPausedValue);
302             mView.setVisibility(View.VISIBLE);
303         }
304     }
305 }