1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
5  * in compliance with the License. You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software distributed under the License
10  * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
11  * or implied. See the License for the specific language governing permissions and limitations under
12  * the License.
13  */
14 package androidx.leanback.transition;
15 
16 import static androidx.annotation.RestrictTo.Scope.LIBRARY;
17 
18 import android.animation.TimeInterpolator;
19 import android.annotation.SuppressLint;
20 import android.app.Fragment;
21 import android.app.FragmentTransaction;
22 import android.content.Context;
23 import android.graphics.Rect;
24 import android.os.Build;
25 import android.transition.AutoTransition;
26 import android.transition.ChangeTransform;
27 import android.transition.Fade;
28 import android.transition.Scene;
29 import android.transition.Transition;
30 import android.transition.TransitionInflater;
31 import android.transition.TransitionManager;
32 import android.transition.TransitionSet;
33 import android.view.View;
34 import android.view.ViewGroup;
35 import android.view.Window;
36 import android.view.animation.AnimationUtils;
37 
38 import androidx.annotation.RestrictTo;
39 
40 import org.jspecify.annotations.NonNull;
41 import org.jspecify.annotations.Nullable;
42 
43 /**
44  * Helper for view transitions.
45  */
46 @RestrictTo(LIBRARY)
47 public final class TransitionHelper {
48 
49     public static final int FADE_IN = 0x1;
50     public static final int FADE_OUT = 0x2;
51 
52     /**
53      * Returns true if system supports entrance Transition animations.
54      */
55     @SuppressWarnings("BooleanMethodIsAlwaysInverted")
systemSupportsEntranceTransitions()56     public static boolean systemSupportsEntranceTransitions() {
57         return Build.VERSION.SDK_INT >= 21;
58     }
59 
60     private static class TransitionStub {
TransitionStub()61         TransitionStub() {
62         }
63     }
64 
getSharedElementEnterTransition(@onNull Window window)65     public static @Nullable Object getSharedElementEnterTransition(@NonNull Window window) {
66         if (Build.VERSION.SDK_INT >= 21) {
67             return window.getSharedElementEnterTransition();
68         }
69         return null;
70     }
71 
setSharedElementEnterTransition( @onNull Window window, @Nullable Object transition )72     public static void setSharedElementEnterTransition(
73             @NonNull Window window,
74             @Nullable Object transition
75     ) {
76         if (Build.VERSION.SDK_INT >= 21) {
77             window.setSharedElementEnterTransition((Transition) transition);
78         }
79     }
80 
getSharedElementReturnTransition(@onNull Window window)81     public static @Nullable Object getSharedElementReturnTransition(@NonNull Window window) {
82         if (Build.VERSION.SDK_INT >= 21) {
83             return window.getSharedElementReturnTransition();
84         }
85         return null;
86     }
87 
setSharedElementReturnTransition( @onNull Window window, @Nullable Object transition )88     public static void setSharedElementReturnTransition(
89             @NonNull Window window,
90             @Nullable Object transition
91     ) {
92         if (Build.VERSION.SDK_INT >= 21) {
93             window.setSharedElementReturnTransition((Transition) transition);
94         }
95     }
96 
getSharedElementExitTransition(@onNull Window window)97     public static @Nullable Object getSharedElementExitTransition(@NonNull Window window) {
98         if (Build.VERSION.SDK_INT >= 21) {
99             return window.getSharedElementExitTransition();
100         }
101         return null;
102     }
103 
getSharedElementReenterTransition(@onNull Window window)104     public static @Nullable Object getSharedElementReenterTransition(@NonNull Window window) {
105         if (Build.VERSION.SDK_INT >= 21) {
106             return window.getSharedElementReenterTransition();
107         }
108         return null;
109     }
110 
getEnterTransition(@onNull Window window)111     public static @Nullable Object getEnterTransition(@NonNull Window window) {
112         if (Build.VERSION.SDK_INT >= 21) {
113             return window.getEnterTransition();
114         }
115         return null;
116     }
117 
setEnterTransition(@onNull Window window, @Nullable Object transition)118     public static void setEnterTransition(@NonNull Window window, @Nullable Object transition) {
119         if (Build.VERSION.SDK_INT >= 21) {
120             window.setEnterTransition((Transition) transition);
121         }
122     }
123 
getReturnTransition(@onNull Window window)124     public static @Nullable Object getReturnTransition(@NonNull Window window) {
125         if (Build.VERSION.SDK_INT >= 21) {
126             return window.getReturnTransition();
127         }
128         return null;
129     }
130 
setReturnTransition(@onNull Window window, @Nullable Object transition)131     public static void setReturnTransition(@NonNull Window window, @Nullable Object transition) {
132         if (Build.VERSION.SDK_INT >= 21) {
133             window.setReturnTransition((Transition) transition);
134         }
135     }
136 
getExitTransition(@onNull Window window)137     public static @Nullable Object getExitTransition(@NonNull Window window) {
138         if (Build.VERSION.SDK_INT >= 21) {
139             return window.getExitTransition();
140         }
141         return null;
142     }
143 
getReenterTransition(@onNull Window window)144     public static @Nullable Object getReenterTransition(@NonNull Window window) {
145         if (Build.VERSION.SDK_INT >= 21) {
146             return window.getReenterTransition();
147         }
148         return null;
149     }
150 
createScene(@onNull ViewGroup sceneRoot, @Nullable Runnable r)151     public static @Nullable Object createScene(@NonNull ViewGroup sceneRoot, @Nullable Runnable r) {
152         Scene scene = new Scene(sceneRoot);
153         scene.setEnterAction(r);
154         return scene;
155     }
156 
createChangeBounds(boolean reparent)157     public static @NonNull Object createChangeBounds(boolean reparent) {
158         CustomChangeBounds changeBounds = new CustomChangeBounds();
159         changeBounds.setReparent(reparent);
160         return changeBounds;
161     }
162 
createChangeTransform()163     public static @NonNull Object createChangeTransform() {
164         if (Build.VERSION.SDK_INT >= 21) {
165             return new ChangeTransform();
166         }
167         return new TransitionStub();
168     }
169 
setChangeBoundsStartDelay( @onNull Object changeBounds, @NonNull View view, int startDelay )170     public static void setChangeBoundsStartDelay(
171             @NonNull Object changeBounds,
172             @NonNull View view,
173             int startDelay
174     ) {
175         ((CustomChangeBounds) changeBounds).setStartDelay(view, startDelay);
176     }
177 
setChangeBoundsStartDelay( @onNull Object changeBounds, int viewId, int startDelay )178     public static void setChangeBoundsStartDelay(
179             @NonNull Object changeBounds,
180             int viewId,
181             int startDelay
182     ) {
183         ((CustomChangeBounds) changeBounds).setStartDelay(viewId, startDelay);
184     }
185 
setChangeBoundsStartDelay( @onNull Object changeBounds, @NonNull String className, int startDelay )186     public static void setChangeBoundsStartDelay(
187             @NonNull Object changeBounds,
188             @NonNull String className,
189             int startDelay
190     ) {
191         ((CustomChangeBounds) changeBounds).setStartDelay(className, startDelay);
192     }
193 
setChangeBoundsDefaultStartDelay( @onNull Object changeBounds, int startDelay )194     public static void setChangeBoundsDefaultStartDelay(
195             @NonNull Object changeBounds,
196             int startDelay
197     ) {
198         ((CustomChangeBounds) changeBounds).setDefaultStartDelay(startDelay);
199     }
200 
createTransitionSet(boolean sequential)201     public static @NonNull Object createTransitionSet(boolean sequential) {
202         TransitionSet set = new TransitionSet();
203         set.setOrdering(sequential ? TransitionSet.ORDERING_SEQUENTIAL
204                 : TransitionSet.ORDERING_TOGETHER);
205         return set;
206     }
207 
createSlide(int slideEdge)208     public static @NonNull Object createSlide(int slideEdge) {
209         SlideKitkat slide = new SlideKitkat();
210         slide.setSlideEdge(slideEdge);
211         return slide;
212     }
213 
createScale()214     public static @NonNull Object createScale() {
215         if (Build.VERSION.SDK_INT >= 21) {
216             return new ChangeTransform();
217         }
218         return new Scale();
219     }
220 
addTransition(@onNull Object transitionSet, @NonNull Object transition)221     public static void addTransition(@NonNull Object transitionSet, @NonNull Object transition) {
222         ((TransitionSet) transitionSet).addTransition((Transition) transition);
223     }
224 
exclude(@onNull Object transition, int targetId, boolean exclude)225     public static void exclude(@NonNull Object transition, int targetId, boolean exclude) {
226         ((Transition) transition).excludeTarget(targetId, exclude);
227     }
228 
exclude( @onNull Object transition, @NonNull View targetView, boolean exclude )229     public static void exclude(
230             @NonNull Object transition,
231             @NonNull View targetView,
232             boolean exclude
233     ) {
234         ((Transition) transition).excludeTarget(targetView, exclude);
235     }
236 
excludeChildren(@onNull Object transition, int targetId, boolean exclude)237     public static void excludeChildren(@NonNull Object transition, int targetId, boolean exclude) {
238         ((Transition) transition).excludeChildren(targetId, exclude);
239     }
240 
excludeChildren( @onNull Object transition, @NonNull View targetView, boolean exclude )241     public static void excludeChildren(
242             @NonNull Object transition,
243             @NonNull View targetView,
244             boolean exclude
245     ) {
246         ((Transition) transition).excludeChildren(targetView, exclude);
247     }
248 
include(@onNull Object transition, int targetId)249     public static void include(@NonNull Object transition, int targetId) {
250         ((Transition) transition).addTarget(targetId);
251     }
252 
include(@onNull Object transition, @NonNull View targetView)253     public static void include(@NonNull Object transition, @NonNull View targetView) {
254         ((Transition) transition).addTarget(targetView);
255     }
256 
setStartDelay(@onNull Object transition, long startDelay)257     public static void setStartDelay(@NonNull Object transition, long startDelay) {
258         ((Transition) transition).setStartDelay(startDelay);
259     }
260 
setDuration(@onNull Object transition, long duration)261     public static void setDuration(@NonNull Object transition, long duration) {
262         ((Transition) transition).setDuration(duration);
263     }
264 
createAutoTransition()265     public static @NonNull Object createAutoTransition() {
266         return new AutoTransition();
267     }
268 
createFadeTransition(int fadeMode)269     public static @NonNull Object createFadeTransition(int fadeMode) {
270         return new Fade(fadeMode);
271     }
272 
addTransitionListener( @onNull Object transition, final @Nullable TransitionListener listener )273     public static void addTransitionListener(
274             @NonNull Object transition,
275             final @Nullable TransitionListener listener
276     ) {
277         if (listener == null) {
278             return;
279         }
280         Transition t = (Transition) transition;
281         listener.mImpl = new Transition.TransitionListener() {
282             @Override
283             public void onTransitionStart(Transition transition11) {
284                 listener.onTransitionStart(transition11);
285             }
286 
287             @Override
288             public void onTransitionResume(Transition transition11) {
289                 listener.onTransitionResume(transition11);
290             }
291 
292             @Override
293             public void onTransitionPause(Transition transition11) {
294                 listener.onTransitionPause(transition11);
295             }
296 
297             @Override
298             public void onTransitionEnd(Transition transition11) {
299                 listener.onTransitionEnd(transition11);
300             }
301 
302             @Override
303             public void onTransitionCancel(Transition transition11) {
304                 listener.onTransitionCancel(transition11);
305             }
306         };
307         t.addListener((Transition.TransitionListener) listener.mImpl);
308     }
309 
removeTransitionListener( @onNull Object transition, @Nullable TransitionListener listener )310     public static void removeTransitionListener(
311             @NonNull Object transition,
312             @Nullable TransitionListener listener
313     ) {
314         if (listener == null || listener.mImpl == null) {
315             return;
316         }
317         Transition t = (Transition) transition;
318         t.removeListener((Transition.TransitionListener) listener.mImpl);
319         listener.mImpl = null;
320     }
321 
runTransition(@ullable Object scene, @Nullable Object transition)322     public static void runTransition(@Nullable Object scene, @Nullable Object transition) {
323         TransitionManager.go((Scene) scene, (Transition) transition);
324     }
325 
setInterpolator( @onNull Object transition, @Nullable Object timeInterpolator )326     public static void setInterpolator(
327             @NonNull Object transition,
328             @Nullable Object timeInterpolator
329     ) {
330         ((Transition) transition).setInterpolator((TimeInterpolator) timeInterpolator);
331     }
332 
addTarget(@onNull Object transition, @NonNull View view)333     public static void addTarget(@NonNull Object transition, @NonNull View view) {
334         ((Transition) transition).addTarget(view);
335     }
336 
createDefaultInterpolator(@onNull Context context)337     public static @Nullable Object createDefaultInterpolator(@NonNull Context context) {
338         if (Build.VERSION.SDK_INT >= 21) {
339             return AnimationUtils.loadInterpolator(context,
340                     android.R.interpolator.fast_out_linear_in);
341         }
342         return null;
343     }
344 
loadTransition(@onNull Context context, int resId)345     public static @NonNull Object loadTransition(@NonNull Context context, int resId) {
346         return TransitionInflater.from(context).inflateTransition(resId);
347     }
348 
349     @SuppressLint("ReferencesDeprecated")
setEnterTransition( @onNull Fragment fragment, @Nullable Object transition )350     public static void setEnterTransition(
351             @NonNull Fragment fragment,
352             @Nullable Object transition
353     ) {
354         if (Build.VERSION.SDK_INT >= 21) {
355             fragment.setEnterTransition((Transition) transition);
356         }
357     }
358 
359     @SuppressLint("ReferencesDeprecated")
setExitTransition( @onNull Fragment fragment, @Nullable Object transition )360     public static void setExitTransition(
361             @NonNull Fragment fragment,
362             @Nullable Object transition
363     ) {
364         if (Build.VERSION.SDK_INT >= 21) {
365             fragment.setExitTransition((Transition) transition);
366         }
367     }
368 
369     @SuppressLint("ReferencesDeprecated")
setSharedElementEnterTransition( @onNull Fragment fragment, @Nullable Object transition )370     public static void setSharedElementEnterTransition(
371             @NonNull Fragment fragment,
372             @Nullable Object transition
373     ) {
374         if (Build.VERSION.SDK_INT >= 21) {
375             fragment.setSharedElementEnterTransition((Transition) transition);
376         }
377     }
378 
379     @SuppressLint("ReferencesDeprecated")
addSharedElement( @onNull FragmentTransaction ft, @NonNull View view, @NonNull String transitionName )380     public static void addSharedElement(
381             @NonNull FragmentTransaction ft,
382             @NonNull View view,
383             @NonNull String transitionName
384     ) {
385         if (Build.VERSION.SDK_INT >= 21) {
386             ft.addSharedElement(view, transitionName);
387         }
388     }
389 
createFadeAndShortSlide(int edge)390     public static @NonNull Object createFadeAndShortSlide(int edge) {
391         if (Build.VERSION.SDK_INT >= 21) {
392             return new FadeAndShortSlide(edge);
393         }
394         return new TransitionStub();
395     }
396 
createFadeAndShortSlide(int edge, float distance)397     public static @NonNull Object createFadeAndShortSlide(int edge, float distance) {
398         if (Build.VERSION.SDK_INT >= 21) {
399             FadeAndShortSlide slide = new FadeAndShortSlide(edge);
400             slide.setDistance(distance);
401             return slide;
402         }
403         return new TransitionStub();
404     }
405 
beginDelayedTransition( @onNull ViewGroup sceneRoot, @Nullable Object transitionObject )406     public static void beginDelayedTransition(
407             @NonNull ViewGroup sceneRoot,
408             @Nullable Object transitionObject
409     ) {
410         if (Build.VERSION.SDK_INT >= 21) {
411             Transition transition = (Transition) transitionObject;
412             TransitionManager.beginDelayedTransition(sceneRoot, transition);
413         }
414     }
415 
setTransitionGroup(@onNull ViewGroup viewGroup, boolean transitionGroup)416     public static void setTransitionGroup(@NonNull ViewGroup viewGroup, boolean transitionGroup) {
417         if (Build.VERSION.SDK_INT >= 21) {
418             viewGroup.setTransitionGroup(transitionGroup);
419         }
420     }
421 
setEpicenterCallback( @onNull Object transition, final @Nullable TransitionEpicenterCallback callback )422     public static void setEpicenterCallback(
423             @NonNull Object transition,
424             final @Nullable TransitionEpicenterCallback callback
425     ) {
426         if (Build.VERSION.SDK_INT >= 21) {
427             if (callback == null) {
428                 ((Transition) transition).setEpicenterCallback(null);
429             } else {
430                 ((Transition) transition).setEpicenterCallback(new Transition.EpicenterCallback() {
431                     @Override
432                     public Rect onGetEpicenter(Transition transition11) {
433                         return callback.onGetEpicenter(transition11);
434                     }
435                 });
436             }
437         }
438     }
439 
TransitionHelper()440     private TransitionHelper() {
441     }
442 }
443