1/*
2 * Copyright (C) 2017 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
17package androidx.transition;
18
19import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;
20
21import android.graphics.Rect;
22import android.view.View;
23import android.view.ViewGroup;
24
25import androidx.annotation.NonNull;
26import androidx.annotation.RestrictTo;
27import androidx.fragment.app.FragmentTransitionImpl;
28
29import java.util.ArrayList;
30import java.util.List;
31
32
33/**
34 * @hide
35 */
36// This is instantiated in androidx.fragment.app.FragmentTransition
37@SuppressWarnings("unused")
38@RestrictTo(LIBRARY_GROUP)
39public class FragmentTransitionSupport extends FragmentTransitionImpl {
40
41    @Override
42    public boolean canHandle(Object transition) {
43        return transition instanceof Transition;
44    }
45
46    @Override
47    public Object cloneTransition(Object transition) {
48        Transition copy = null;
49        if (transition != null) {
50            copy = ((Transition) transition).clone();
51        }
52        return copy;
53    }
54
55    @Override
56    public Object wrapTransitionInSet(Object transition) {
57        if (transition == null) {
58            return null;
59        }
60        TransitionSet transitionSet = new TransitionSet();
61        transitionSet.addTransition((Transition) transition);
62        return transitionSet;
63    }
64
65    @Override
66    public void setSharedElementTargets(Object transitionObj,
67            View nonExistentView, ArrayList<View> sharedViews) {
68        TransitionSet transition = (TransitionSet) transitionObj;
69        final List<View> views = transition.getTargets();
70        views.clear();
71        final int count = sharedViews.size();
72        for (int i = 0; i < count; i++) {
73            final View view = sharedViews.get(i);
74            bfsAddViewChildren(views, view);
75        }
76        views.add(nonExistentView);
77        sharedViews.add(nonExistentView);
78        addTargets(transition, sharedViews);
79    }
80
81    @Override
82    public void setEpicenter(Object transitionObj, View view) {
83        if (view != null) {
84            Transition transition = (Transition) transitionObj;
85            final Rect epicenter = new Rect();
86            getBoundsOnScreen(view, epicenter);
87
88            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
89                @Override
90                public Rect onGetEpicenter(@NonNull Transition transition) {
91                    return epicenter;
92                }
93            });
94        }
95    }
96
97    @Override
98    public void addTargets(Object transitionObj, ArrayList<View> views) {
99        Transition transition = (Transition) transitionObj;
100        if (transition == null) {
101            return;
102        }
103        if (transition instanceof TransitionSet) {
104            TransitionSet set = (TransitionSet) transition;
105            int numTransitions = set.getTransitionCount();
106            for (int i = 0; i < numTransitions; i++) {
107                Transition child = set.getTransitionAt(i);
108                addTargets(child, views);
109            }
110        } else if (!hasSimpleTarget(transition)) {
111            List<View> targets = transition.getTargets();
112            if (isNullOrEmpty(targets)) {
113                // We can just add the target views
114                int numViews = views.size();
115                for (int i = 0; i < numViews; i++) {
116                    transition.addTarget(views.get(i));
117                }
118            }
119        }
120    }
121
122    private static boolean hasSimpleTarget(Transition transition) {
123        return !isNullOrEmpty(transition.getTargetIds())
124                || !isNullOrEmpty(transition.getTargetNames())
125                || !isNullOrEmpty(transition.getTargetTypes());
126    }
127
128    @Override
129    public Object mergeTransitionsTogether(Object transition1, Object transition2,
130            Object transition3) {
131        TransitionSet transitionSet = new TransitionSet();
132        if (transition1 != null) {
133            transitionSet.addTransition((Transition) transition1);
134        }
135        if (transition2 != null) {
136            transitionSet.addTransition((Transition) transition2);
137        }
138        if (transition3 != null) {
139            transitionSet.addTransition((Transition) transition3);
140        }
141        return transitionSet;
142    }
143
144    @Override
145    public void scheduleHideFragmentView(Object exitTransitionObj, final View fragmentView,
146            final ArrayList<View> exitingViews) {
147        Transition exitTransition = (Transition) exitTransitionObj;
148        exitTransition.addListener(new Transition.TransitionListener() {
149            @Override
150            public void onTransitionStart(@NonNull Transition transition) {
151            }
152
153            @Override
154            public void onTransitionEnd(@NonNull Transition transition) {
155                transition.removeListener(this);
156                fragmentView.setVisibility(View.GONE);
157                final int numViews = exitingViews.size();
158                for (int i = 0; i < numViews; i++) {
159                    exitingViews.get(i).setVisibility(View.VISIBLE);
160                }
161            }
162
163            @Override
164            public void onTransitionCancel(@NonNull Transition transition) {
165            }
166
167            @Override
168            public void onTransitionPause(@NonNull Transition transition) {
169            }
170
171            @Override
172            public void onTransitionResume(@NonNull Transition transition) {
173            }
174        });
175    }
176
177    @Override
178    public Object mergeTransitionsInSequence(Object exitTransitionObj,
179            Object enterTransitionObj, Object sharedElementTransitionObj) {
180        // First do exit, then enter, but allow shared element transition to happen
181        // during both.
182        Transition staggered = null;
183        final Transition exitTransition = (Transition) exitTransitionObj;
184        final Transition enterTransition = (Transition) enterTransitionObj;
185        final Transition sharedElementTransition = (Transition) sharedElementTransitionObj;
186        if (exitTransition != null && enterTransition != null) {
187            staggered = new TransitionSet()
188                    .addTransition(exitTransition)
189                    .addTransition(enterTransition)
190                    .setOrdering(TransitionSet.ORDERING_SEQUENTIAL);
191        } else if (exitTransition != null) {
192            staggered = exitTransition;
193        } else if (enterTransition != null) {
194            staggered = enterTransition;
195        }
196        if (sharedElementTransition != null) {
197            TransitionSet together = new TransitionSet();
198            if (staggered != null) {
199                together.addTransition(staggered);
200            }
201            together.addTransition(sharedElementTransition);
202            return together;
203        } else {
204            return staggered;
205        }
206    }
207
208    @Override
209    public void beginDelayedTransition(ViewGroup sceneRoot, Object transition) {
210        TransitionManager.beginDelayedTransition(sceneRoot, (Transition) transition);
211    }
212
213    @Override
214    public void scheduleRemoveTargets(final Object overallTransitionObj,
215            final Object enterTransition, final ArrayList<View> enteringViews,
216            final Object exitTransition, final ArrayList<View> exitingViews,
217            final Object sharedElementTransition, final ArrayList<View> sharedElementsIn) {
218        final Transition overallTransition = (Transition) overallTransitionObj;
219        overallTransition.addListener(new Transition.TransitionListener() {
220            @Override
221            public void onTransitionStart(@NonNull Transition transition) {
222                if (enterTransition != null) {
223                    replaceTargets(enterTransition, enteringViews, null);
224                }
225                if (exitTransition != null) {
226                    replaceTargets(exitTransition, exitingViews, null);
227                }
228                if (sharedElementTransition != null) {
229                    replaceTargets(sharedElementTransition, sharedElementsIn, null);
230                }
231            }
232
233            @Override
234            public void onTransitionEnd(@NonNull Transition transition) {
235            }
236
237            @Override
238            public void onTransitionCancel(@NonNull Transition transition) {
239            }
240
241            @Override
242            public void onTransitionPause(@NonNull Transition transition) {
243            }
244
245            @Override
246            public void onTransitionResume(@NonNull Transition transition) {
247            }
248        });
249    }
250
251    @Override
252    public void swapSharedElementTargets(Object sharedElementTransitionObj,
253            ArrayList<View> sharedElementsOut, ArrayList<View> sharedElementsIn) {
254        TransitionSet sharedElementTransition = (TransitionSet) sharedElementTransitionObj;
255        if (sharedElementTransition != null) {
256            sharedElementTransition.getTargets().clear();
257            sharedElementTransition.getTargets().addAll(sharedElementsIn);
258            replaceTargets(sharedElementTransition, sharedElementsOut, sharedElementsIn);
259        }
260    }
261
262    @Override
263    public void replaceTargets(Object transitionObj, ArrayList<View> oldTargets,
264            ArrayList<View> newTargets) {
265        Transition transition = (Transition) transitionObj;
266        if (transition instanceof TransitionSet) {
267            TransitionSet set = (TransitionSet) transition;
268            int numTransitions = set.getTransitionCount();
269            for (int i = 0; i < numTransitions; i++) {
270                Transition child = set.getTransitionAt(i);
271                replaceTargets(child, oldTargets, newTargets);
272            }
273        } else if (!hasSimpleTarget(transition)) {
274            List<View> targets = transition.getTargets();
275            if (targets.size() == oldTargets.size()
276                    && targets.containsAll(oldTargets)) {
277                // We have an exact match. We must have added these earlier in addTargets
278                final int targetCount = newTargets == null ? 0 : newTargets.size();
279                for (int i = 0; i < targetCount; i++) {
280                    transition.addTarget(newTargets.get(i));
281                }
282                for (int i = oldTargets.size() - 1; i >= 0; i--) {
283                    transition.removeTarget(oldTargets.get(i));
284                }
285            }
286        }
287    }
288
289    @Override
290    public void addTarget(Object transitionObj, View view) {
291        if (transitionObj != null) {
292            Transition transition = (Transition) transitionObj;
293            transition.addTarget(view);
294        }
295    }
296
297    @Override
298    public void removeTarget(Object transitionObj, View view) {
299        if (transitionObj != null) {
300            Transition transition = (Transition) transitionObj;
301            transition.removeTarget(view);
302        }
303    }
304
305    @Override
306    public void setEpicenter(Object transitionObj, final Rect epicenter) {
307        if (transitionObj != null) {
308            Transition transition = (Transition) transitionObj;
309            transition.setEpicenterCallback(new Transition.EpicenterCallback() {
310                @Override
311                public Rect onGetEpicenter(@NonNull Transition transition) {
312                    if (epicenter == null || epicenter.isEmpty()) {
313                        return null;
314                    }
315                    return epicenter;
316                }
317            });
318        }
319    }
320
321}
322