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