Transition.java revision d4c3c91dd0757eec9703ef90ea4c5a7ee99f18ca
1/*
2 * Copyright (C) 2013 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.transition;
18
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.TimeInterpolator;
22import android.graphics.Rect;
23import android.util.ArrayMap;
24import android.util.Log;
25import android.util.LongSparseArray;
26import android.util.SparseArray;
27import android.util.SparseLongArray;
28import android.view.SurfaceView;
29import android.view.TextureView;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.ViewOverlay;
33import android.view.WindowId;
34import android.widget.ListView;
35import android.widget.Spinner;
36
37import java.util.ArrayList;
38import java.util.List;
39
40/**
41 * A Transition holds information about animations that will be run on its
42 * targets during a scene change. Subclasses of this abstract class may
43 * choreograph several child transitions ({@link TransitionSet} or they may
44 * perform custom animations themselves. Any Transition has two main jobs:
45 * (1) capture property values, and (2) play animations based on changes to
46 * captured property values. A custom transition knows what property values
47 * on View objects are of interest to it, and also knows how to animate
48 * changes to those values. For example, the {@link Fade} transition tracks
49 * changes to visibility-related properties and is able to construct and run
50 * animations that fade items in or out based on changes to those properties.
51 *
52 * <p>Note: Transitions may not work correctly with either {@link SurfaceView}
53 * or {@link TextureView}, due to the way that these views are displayed
54 * on the screen. For SurfaceView, the problem is that the view is updated from
55 * a non-UI thread, so changes to the view due to transitions (such as moving
56 * and resizing the view) may be out of sync with the display inside those bounds.
57 * TextureView is more compatible with transitions in general, but some
58 * specific transitions (such as {@link Fade}) may not be compatible
59 * with TextureView because they rely on {@link ViewOverlay} functionality,
60 * which does not currently work with TextureView.</p>
61 *
62 * <p>Transitions can be declared in XML resource files inside the <code>res/transition</code>
63 * directory. Transition resources consist of a tag name for one of the Transition
64 * subclasses along with attributes to define some of the attributes of that transition.
65 * For example, here is a minimal resource file that declares a {@link ChangeBounds} transition:
66 *
67 * {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds}
68 *
69 * <p>This TransitionSet contains {@link android.transition.Explode} for visibility,
70 * {@link android.transition.ChangeBounds}, {@link android.transition.ChangeTransform},
71 * and {@link android.transition.ChangeClipBounds} for non-<code>ImageView</code>s and
72 * {@link android.transition.MoveImage} for <code>ImageView</code>s:</p>
73 *
74 * {@sample development/samples/ApiDemos/res/transition/explode_move_together.xml MultipleTransform}
75 *
76 * <p>Note that attributes for the transition are not required, just as they are
77 * optional when declared in code; Transitions created from XML resources will use
78 * the same defaults as their code-created equivalents. Here is a slightly more
79 * elaborate example which declares a {@link TransitionSet} transition with
80 * {@link ChangeBounds} and {@link Fade} child transitions:</p>
81 *
82 * {@sample
83 * development/samples/ApiDemos/res/transition/changebounds_fadeout_sequential.xml TransitionSet}
84 *
85 * <p>In this example, the transitionOrdering attribute is used on the TransitionSet
86 * object to change from the default {@link TransitionSet#ORDERING_TOGETHER} behavior
87 * to be {@link TransitionSet#ORDERING_SEQUENTIAL} instead. Also, the {@link Fade}
88 * transition uses a fadingMode of {@link Fade#OUT} instead of the default
89 * out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which
90 * takes a set of {@link android.R.styleable#TransitionTarget target} tags, each
91 * of which lists a specific <code>targetId</code>, <code>targetClass</code>,
92 * <code>targetViewName</code>, <code>excludeId</code>, <code>excludeClass</code>, or
93 * <code>excludeViewName</code>, which this transition acts upon.
94 * Use of targets is optional, but can be used to either limit the time spent checking
95 * attributes on unchanging views, or limiting the types of animations run on specific views.
96 * In this case, we know that only the <code>grayscaleContainer</code> will be
97 * disappearing, so we choose to limit the {@link Fade} transition to only that view.</p>
98 *
99 * Further information on XML resource descriptions for transitions can be found for
100 * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
101 * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade}, and
102 * {@link android.R.styleable#Slide}.
103 *
104 */
105public abstract class Transition implements Cloneable {
106
107    private static final String LOG_TAG = "Transition";
108    static final boolean DBG = false;
109
110    /**
111     * With {@link #setMatchOrder(int...)}, chooses to match by View instance.
112     */
113    public static final int MATCH_INSTANCE = 0x1;
114    private static final int MATCH_FIRST = MATCH_INSTANCE;
115
116    /**
117     * With {@link #setMatchOrder(int...)}, chooses to match by
118     * {@link android.view.View#getViewName()}. Null names will not be matched.
119     */
120    public static final int MATCH_VIEW_NAME = 0x2;
121
122    /**
123     * With {@link #setMatchOrder(int...)}, chooses to match by
124     * {@link android.view.View#getId()}. Negative IDs will not be matched.
125     */
126    public static final int MATCH_ID = 0x3;
127
128    /**
129     * With {@link #setMatchOrder(int...)}, chooses to match by the {@link android.widget.Adapter}
130     * item id. When {@link android.widget.Adapter#hasStableIds()} returns false, no match
131     * will be made for items.
132     */
133    public static final int MATCH_ITEM_ID = 0x4;
134
135    private static final int MATCH_LAST = MATCH_ITEM_ID;
136
137    private static final int[] DEFAULT_MATCH_ORDER = {
138        MATCH_VIEW_NAME,
139        MATCH_INSTANCE,
140        MATCH_ID,
141        MATCH_ITEM_ID,
142    };
143
144    private String mName = getClass().getName();
145
146    long mStartDelay = -1;
147    long mDuration = -1;
148    TimeInterpolator mInterpolator = null;
149    ArrayList<Integer> mTargetIds = new ArrayList<Integer>();
150    ArrayList<View> mTargets = new ArrayList<View>();
151    ArrayList<String> mTargetNames = null;
152    ArrayList<Class> mTargetTypes = null;
153    ArrayList<Integer> mTargetIdExcludes = null;
154    ArrayList<View> mTargetExcludes = null;
155    ArrayList<Class> mTargetTypeExcludes = null;
156    ArrayList<String> mTargetNameExcludes = null;
157    ArrayList<Integer> mTargetIdChildExcludes = null;
158    ArrayList<View> mTargetChildExcludes = null;
159    ArrayList<Class> mTargetTypeChildExcludes = null;
160    private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
161    private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
162    TransitionSet mParent = null;
163    private int[] mMatchOrder = DEFAULT_MATCH_ORDER;
164
165    // Per-animator information used for later canceling when future transitions overlap
166    private static ThreadLocal<ArrayMap<Animator, AnimationInfo>> sRunningAnimators =
167            new ThreadLocal<ArrayMap<Animator, AnimationInfo>>();
168
169    // Scene Root is set at createAnimator() time in the cloned Transition
170    ViewGroup mSceneRoot = null;
171
172    // Whether removing views from their parent is possible. This is only for views
173    // in the start scene, which are no longer in the view hierarchy. This property
174    // is determined by whether the previous Scene was created from a layout
175    // resource, and thus the views from the exited scene are going away anyway
176    // and can be removed as necessary to achieve a particular effect, such as
177    // removing them from parents to add them to overlays.
178    boolean mCanRemoveViews = false;
179
180    // Track all animators in use in case the transition gets canceled and needs to
181    // cancel running animators
182    private ArrayList<Animator> mCurrentAnimators = new ArrayList<Animator>();
183
184    // Number of per-target instances of this Transition currently running. This count is
185    // determined by calls to start() and end()
186    int mNumInstances = 0;
187
188    // Whether this transition is currently paused, due to a call to pause()
189    boolean mPaused = false;
190
191    // Whether this transition has ended. Used to avoid pause/resume on transitions
192    // that have completed
193    private boolean mEnded = false;
194
195    // The set of listeners to be sent transition lifecycle events.
196    ArrayList<TransitionListener> mListeners = null;
197
198    // The set of animators collected from calls to createAnimator(),
199    // to be run in runAnimators()
200    ArrayList<Animator> mAnimators = new ArrayList<Animator>();
201
202    // The function for calculating the Animation start delay.
203    TransitionPropagation mPropagation;
204
205    // The rectangular region for Transitions like Explode and TransitionPropagations
206    // like CircularPropagation
207    EpicenterCallback mEpicenterCallback;
208
209    // For Fragment shared element transitions, linking views explicitly by mismatching
210    // viewNames.
211    ArrayMap<String, String> mNameOverrides;
212
213    /**
214     * Constructs a Transition object with no target objects. A transition with
215     * no targets defaults to running on all target objects in the scene hierarchy
216     * (if the transition is not contained in a TransitionSet), or all target
217     * objects passed down from its parent (if it is in a TransitionSet).
218     */
219    public Transition() {}
220
221    /**
222     * Sets the duration of this transition. By default, there is no duration
223     * (indicated by a negative number), which means that the Animator created by
224     * the transition will have its own specified duration. If the duration of a
225     * Transition is set, that duration will override the Animator duration.
226     *
227     * @param duration The length of the animation, in milliseconds.
228     * @return This transition object.
229     * @attr ref android.R.styleable#Transition_duration
230     */
231    public Transition setDuration(long duration) {
232        mDuration = duration;
233        return this;
234    }
235
236    /**
237     * Returns the duration set on this transition. If no duration has been set,
238     * the returned value will be negative, indicating that resulting animators will
239     * retain their own durations.
240     *
241     * @return The duration set on this transition, in milliseconds, if one has been
242     * set, otherwise returns a negative number.
243     */
244    public long getDuration() {
245        return mDuration;
246    }
247
248    /**
249     * Sets the startDelay of this transition. By default, there is no delay
250     * (indicated by a negative number), which means that the Animator created by
251     * the transition will have its own specified startDelay. If the delay of a
252     * Transition is set, that delay will override the Animator delay.
253     *
254     * @param startDelay The length of the delay, in milliseconds.
255     * @return This transition object.
256     * @attr ref android.R.styleable#Transition_startDelay
257     */
258    public Transition setStartDelay(long startDelay) {
259        mStartDelay = startDelay;
260        return this;
261    }
262
263    /**
264     * Returns the startDelay set on this transition. If no startDelay has been set,
265     * the returned value will be negative, indicating that resulting animators will
266     * retain their own startDelays.
267     *
268     * @return The startDelay set on this transition, in milliseconds, if one has
269     * been set, otherwise returns a negative number.
270     */
271    public long getStartDelay() {
272        return mStartDelay;
273    }
274
275    /**
276     * Sets the interpolator of this transition. By default, the interpolator
277     * is null, which means that the Animator created by the transition
278     * will have its own specified interpolator. If the interpolator of a
279     * Transition is set, that interpolator will override the Animator interpolator.
280     *
281     * @param interpolator The time interpolator used by the transition
282     * @return This transition object.
283     * @attr ref android.R.styleable#Transition_interpolator
284     */
285    public Transition setInterpolator(TimeInterpolator interpolator) {
286        mInterpolator = interpolator;
287        return this;
288    }
289
290    /**
291     * Returns the interpolator set on this transition. If no interpolator has been set,
292     * the returned value will be null, indicating that resulting animators will
293     * retain their own interpolators.
294     *
295     * @return The interpolator set on this transition, if one has been set, otherwise
296     * returns null.
297     */
298    public TimeInterpolator getInterpolator() {
299        return mInterpolator;
300    }
301
302    /**
303     * Returns the set of property names used stored in the {@link TransitionValues}
304     * object passed into {@link #captureStartValues(TransitionValues)} that
305     * this transition cares about for the purposes of canceling overlapping animations.
306     * When any transition is started on a given scene root, all transitions
307     * currently running on that same scene root are checked to see whether the
308     * properties on which they based their animations agree with the end values of
309     * the same properties in the new transition. If the end values are not equal,
310     * then the old animation is canceled since the new transition will start a new
311     * animation to these new values. If the values are equal, the old animation is
312     * allowed to continue and no new animation is started for that transition.
313     *
314     * <p>A transition does not need to override this method. However, not doing so
315     * will mean that the cancellation logic outlined in the previous paragraph
316     * will be skipped for that transition, possibly leading to artifacts as
317     * old transitions and new transitions on the same targets run in parallel,
318     * animating views toward potentially different end values.</p>
319     *
320     * @return An array of property names as described in the class documentation for
321     * {@link TransitionValues}. The default implementation returns <code>null</code>.
322     */
323    public String[] getTransitionProperties() {
324        return null;
325    }
326
327    /**
328     * This method creates an animation that will be run for this transition
329     * given the information in the startValues and endValues structures captured
330     * earlier for the start and end scenes. Subclasses of Transition should override
331     * this method. The method should only be called by the transition system; it is
332     * not intended to be called from external classes.
333     *
334     * <p>This method is called by the transition's parent (all the way up to the
335     * topmost Transition in the hierarchy) with the sceneRoot and start/end
336     * values that the transition may need to set up initial target values
337     * and construct an appropriate animation. For example, if an overall
338     * Transition is a {@link TransitionSet} consisting of several
339     * child transitions in sequence, then some of the child transitions may
340     * want to set initial values on target views prior to the overall
341     * Transition commencing, to put them in an appropriate state for the
342     * delay between that start and the child Transition start time. For
343     * example, a transition that fades an item in may wish to set the starting
344     * alpha value to 0, to avoid it blinking in prior to the transition
345     * actually starting the animation. This is necessary because the scene
346     * change that triggers the Transition will automatically set the end-scene
347     * on all target views, so a Transition that wants to animate from a
348     * different value should set that value prior to returning from this method.</p>
349     *
350     * <p>Additionally, a Transition can perform logic to determine whether
351     * the transition needs to run on the given target and start/end values.
352     * For example, a transition that resizes objects on the screen may wish
353     * to avoid running for views which are not present in either the start
354     * or end scenes.</p>
355     *
356     * <p>If there is an animator created and returned from this method, the
357     * transition mechanism will apply any applicable duration, startDelay,
358     * and interpolator to that animation and start it. A return value of
359     * <code>null</code> indicates that no animation should run. The default
360     * implementation returns null.</p>
361     *
362     * <p>The method is called for every applicable target object, which is
363     * stored in the {@link TransitionValues#view} field.</p>
364     *
365     *
366     * @param sceneRoot The root of the transition hierarchy.
367     * @param startValues The values for a specific target in the start scene.
368     * @param endValues The values for the target in the end scene.
369     * @return A Animator to be started at the appropriate time in the
370     * overall transition for this scene change. A null value means no animation
371     * should be run.
372     */
373    public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
374            TransitionValues endValues) {
375        return null;
376    }
377
378    /**
379     * Sets the order in which Transition matches View start and end values.
380     * <p>
381     * The default behavior is to match first by {@link android.view.View#getViewName()},
382     * then by View instance, then by {@link android.view.View#getId()} and finally
383     * by its item ID if it is in a direct child of ListView. The caller can
384     * choose to have only some or all of the values of {@link #MATCH_INSTANCE},
385     * {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. Only
386     * the match algorithms supplied will be used to determine whether Views are the
387     * the same in both the start and end Scene. Views that do not match will be considered
388     * as entering or leaving the Scene.
389     * </p>
390     * @param matches A list of zero or more of {@link #MATCH_INSTANCE},
391     *                {@link #MATCH_VIEW_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}.
392     *                If none are provided, then the default match order will be set.
393     */
394    public void setMatchOrder(int... matches) {
395        if (matches == null || matches.length == 0) {
396            mMatchOrder = DEFAULT_MATCH_ORDER;
397        } else {
398            for (int i = 0; i < matches.length; i++) {
399                int match = matches[i];
400                if (!isValidMatch(match)) {
401                    throw new IllegalArgumentException("matches contains invalid value");
402                }
403                if (alreadyContains(matches, i)) {
404                    throw new IllegalArgumentException("matches contains a duplicate value");
405                }
406            }
407            mMatchOrder = matches.clone();
408        }
409    }
410
411    private static boolean isValidMatch(int match) {
412        return (match >= MATCH_FIRST && match <= MATCH_LAST);
413    }
414
415    private static boolean alreadyContains(int[] array, int searchIndex) {
416        int value = array[searchIndex];
417        for (int i = 0; i < searchIndex; i++) {
418            if (array[i] == value) {
419                return true;
420            }
421        }
422        return false;
423    }
424
425    /**
426     * Match start/end values by View instance. Adds matched values to startValuesList
427     * and endValuesList and removes them from unmatchedStart and unmatchedEnd.
428     */
429    private void matchInstances(ArrayList<TransitionValues> startValuesList,
430            ArrayList<TransitionValues> endValuesList,
431            ArrayMap<View, TransitionValues> unmatchedStart,
432            ArrayMap<View, TransitionValues> unmatchedEnd) {
433        for (int i = unmatchedStart.size() - 1; i >= 0; i--) {
434            View view = unmatchedStart.keyAt(i);
435            TransitionValues end = unmatchedEnd.remove(view);
436            if (end != null) {
437                TransitionValues start = unmatchedStart.removeAt(i);
438                startValuesList.add(start);
439                endValuesList.add(end);
440            }
441        }
442    }
443
444    /**
445     * Match start/end values by Adapter item ID. Adds matched values to startValuesList
446     * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using
447     * startItemIds and endItemIds as a guide for which Views have unique item IDs.
448     */
449    private void matchItemIds(ArrayList<TransitionValues> startValuesList,
450            ArrayList<TransitionValues> endValuesList,
451            ArrayMap<View, TransitionValues> unmatchedStart,
452            ArrayMap<View, TransitionValues> unmatchedEnd,
453            LongSparseArray<View> startItemIds, LongSparseArray<View> endItemIds) {
454        int numStartIds = startItemIds.size();
455        for (int i = 0; i < numStartIds; i++) {
456            View startView = startItemIds.valueAt(i);
457            if (startView != null) {
458                View endView = endItemIds.get(startItemIds.keyAt(i));
459                if (endView != null) {
460                    TransitionValues startValues = unmatchedStart.get(startView);
461                    TransitionValues endValues = unmatchedEnd.get(endView);
462                    if (startValues != null && endValues != null) {
463                        startValuesList.add(startValues);
464                        endValuesList.add(endValues);
465                        unmatchedStart.remove(startView);
466                        unmatchedEnd.remove(endView);
467                    }
468                }
469            }
470        }
471    }
472
473    /**
474     * Match start/end values by Adapter view ID. Adds matched values to startValuesList
475     * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using
476     * startIds and endIds as a guide for which Views have unique IDs.
477     */
478    private void matchIds(ArrayList<TransitionValues> startValuesList,
479            ArrayList<TransitionValues> endValuesList,
480            ArrayMap<View, TransitionValues> unmatchedStart,
481            ArrayMap<View, TransitionValues> unmatchedEnd,
482            SparseArray<View> startIds, SparseArray<View> endIds) {
483        int numStartIds = startIds.size();
484        for (int i = 0; i < numStartIds; i++) {
485            View startView = startIds.valueAt(i);
486            if (startView != null && isValidTarget(startView)) {
487                View endView = endIds.get(startIds.keyAt(i));
488                if (endView != null && isValidTarget(endView)) {
489                    TransitionValues startValues = unmatchedStart.get(startView);
490                    TransitionValues endValues = unmatchedEnd.get(endView);
491                    if (startValues != null && endValues != null) {
492                        startValuesList.add(startValues);
493                        endValuesList.add(endValues);
494                        unmatchedStart.remove(startView);
495                        unmatchedEnd.remove(endView);
496                    }
497                }
498            }
499        }
500    }
501
502    /**
503     * Match start/end values by Adapter viewName. Adds matched values to startValuesList
504     * and endValuesList and removes them from unmatchedStart and unmatchedEnd, using
505     * startNames and endNames as a guide for which Views have unique viewNames.
506     */
507    private void matchNames(ArrayList<TransitionValues> startValuesList,
508            ArrayList<TransitionValues> endValuesList,
509            ArrayMap<View, TransitionValues> unmatchedStart,
510            ArrayMap<View, TransitionValues> unmatchedEnd,
511            ArrayMap<String, View> startNames, ArrayMap<String, View> endNames) {
512        int numStartNames = startNames.size();
513        for (int i = 0; i < numStartNames; i++) {
514            View startView = startNames.valueAt(i);
515            if (startView != null && isValidTarget(startView)) {
516                View endView = endNames.get(startNames.keyAt(i));
517                if (endView != null && isValidTarget(endView)) {
518                    TransitionValues startValues = unmatchedStart.get(startView);
519                    TransitionValues endValues = unmatchedEnd.get(endView);
520                    if (startValues != null && endValues != null) {
521                        startValuesList.add(startValues);
522                        endValuesList.add(endValues);
523                        unmatchedStart.remove(startView);
524                        unmatchedEnd.remove(endView);
525                    }
526                }
527            }
528        }
529    }
530
531    /**
532     * Adds all values from unmatchedStart and unmatchedEnd to startValuesList and endValuesList,
533     * assuming that there is no match between values in the list.
534     */
535    private void addUnmatched(ArrayList<TransitionValues> startValuesList,
536            ArrayList<TransitionValues> endValuesList,
537            ArrayMap<View, TransitionValues> unmatchedStart,
538            ArrayMap<View, TransitionValues> unmatchedEnd) {
539        // Views that only exist in the start Scene
540        for (int i = 0; i < unmatchedStart.size(); i++) {
541            startValuesList.add(unmatchedStart.valueAt(i));
542            endValuesList.add(null);
543        }
544
545        // Views that only exist in the end Scene
546        for (int i = 0; i < unmatchedEnd.size(); i++) {
547            endValuesList.add(unmatchedEnd.valueAt(i));
548            startValuesList.add(null);
549        }
550    }
551
552    private void matchStartAndEnd(TransitionValuesMaps startValues,
553            TransitionValuesMaps endValues,
554            ArrayList<TransitionValues> startValuesList,
555            ArrayList<TransitionValues> endValuesList) {
556        ArrayMap<View, TransitionValues> unmatchedStart =
557                new ArrayMap<View, TransitionValues>(startValues.viewValues);
558        ArrayMap<View, TransitionValues> unmatchedEnd =
559                new ArrayMap<View, TransitionValues>(endValues.viewValues);
560
561        for (int i = 0; i < mMatchOrder.length; i++) {
562            switch (mMatchOrder[i]) {
563                case MATCH_INSTANCE:
564                    matchInstances(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
565                    break;
566                case MATCH_VIEW_NAME:
567                    matchNames(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
568                            startValues.nameValues, endValues.nameValues);
569                    break;
570                case MATCH_ID:
571                    matchIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
572                            startValues.idValues, endValues.idValues);
573                    break;
574                case MATCH_ITEM_ID:
575                    matchItemIds(startValuesList, endValuesList, unmatchedStart, unmatchedEnd,
576                            startValues.itemIdValues, endValues.itemIdValues);
577                    break;
578            }
579        }
580        addUnmatched(startValuesList, endValuesList, unmatchedStart, unmatchedEnd);
581    }
582
583    /**
584     * This method, essentially a wrapper around all calls to createAnimator for all
585     * possible target views, is called with the entire set of start/end
586     * values. The implementation in Transition iterates through these lists
587     * and calls {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
588     * with each set of start/end values on this transition. The
589     * TransitionSet subclass overrides this method and delegates it to
590     * each of its children in succession.
591     *
592     * @hide
593     */
594    protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
595            TransitionValuesMaps endValues) {
596        if (DBG) {
597            Log.d(LOG_TAG, "createAnimators() for " + this);
598        }
599        ArrayList<TransitionValues> startValuesList = new ArrayList<TransitionValues>();
600        ArrayList<TransitionValues> endValuesList = new ArrayList<TransitionValues>();
601        matchStartAndEnd(startValues, endValues, startValuesList, endValuesList);
602
603        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
604        long minStartDelay = Long.MAX_VALUE;
605        int minAnimator = mAnimators.size();
606        SparseLongArray startDelays = new SparseLongArray();
607        for (int i = 0; i < startValuesList.size(); ++i) {
608            TransitionValues start = startValuesList.get(i);
609            TransitionValues end = endValuesList.get(i);
610            // Only bother trying to animate with valid values that differ between start/end
611            boolean isInvalidStart = start != null && !isValidTarget(start.view);
612            boolean isInvalidEnd = end != null && !isValidTarget(end.view);
613            boolean isChanged = start != end && (start == null || !start.equals(end));
614            if (isChanged && !isInvalidStart && !isInvalidEnd) {
615                if (DBG) {
616                    View view = (end != null) ? end.view : start.view;
617                    Log.d(LOG_TAG, "  differing start/end values for view " + view);
618                    if (start == null || end == null) {
619                        Log.d(LOG_TAG, "    " + ((start == null) ?
620                                "start null, end non-null" : "start non-null, end null"));
621                    } else {
622                        for (String key : start.values.keySet()) {
623                            Object startValue = start.values.get(key);
624                            Object endValue = end.values.get(key);
625                            if (startValue != endValue && !startValue.equals(endValue)) {
626                                Log.d(LOG_TAG, "    " + key + ": start(" + startValue +
627                                        "), end(" + endValue + ")");
628                            }
629                        }
630                    }
631                }
632                // TODO: what to do about targetIds and itemIds?
633                Animator animator = createAnimator(sceneRoot, start, end);
634                if (animator != null) {
635                    // Save animation info for future cancellation purposes
636                    View view = null;
637                    TransitionValues infoValues = null;
638                    if (end != null) {
639                        view = end.view;
640                        String[] properties = getTransitionProperties();
641                        if (view != null && properties != null && properties.length > 0) {
642                            infoValues = new TransitionValues();
643                            infoValues.view = view;
644                            TransitionValues newValues = endValues.viewValues.get(view);
645                            if (newValues != null) {
646                                for (int j = 0; j < properties.length; ++j) {
647                                    infoValues.values.put(properties[j],
648                                            newValues.values.get(properties[j]));
649                                }
650                            }
651                            int numExistingAnims = runningAnimators.size();
652                            for (int j = 0; j < numExistingAnims; ++j) {
653                                Animator anim = runningAnimators.keyAt(j);
654                                AnimationInfo info = runningAnimators.get(anim);
655                                if (info.values != null && info.view == view &&
656                                        ((info.name == null && getName() == null) ||
657                                                info.name.equals(getName()))) {
658                                    if (info.values.equals(infoValues)) {
659                                        // Favor the old animator
660                                        animator = null;
661                                        break;
662                                    }
663                                }
664                            }
665                        }
666                    } else {
667                        view = (start != null) ? start.view : null;
668                    }
669                    if (animator != null) {
670                        if (mPropagation != null) {
671                            long delay = mPropagation
672                                    .getStartDelay(sceneRoot, this, start, end);
673                            startDelays.put(mAnimators.size(), delay);
674                            minStartDelay = Math.min(delay, minStartDelay);
675                        }
676                        AnimationInfo info = new AnimationInfo(view, getName(),
677                                sceneRoot.getWindowId(), infoValues);
678                        runningAnimators.put(animator, info);
679                        mAnimators.add(animator);
680                    }
681                }
682            }
683        }
684        if (minStartDelay != 0) {
685            for (int i = 0; i < startDelays.size(); i++) {
686                int index = startDelays.keyAt(i);
687                Animator animator = mAnimators.get(index);
688                long delay = startDelays.valueAt(i) - minStartDelay + animator.getStartDelay();
689                animator.setStartDelay(delay);
690            }
691        }
692    }
693
694    /**
695     * Internal utility method for checking whether a given view/id
696     * is valid for this transition, where "valid" means that either
697     * the Transition has no target/targetId list (the default, in which
698     * cause the transition should act on all views in the hiearchy), or
699     * the given view is in the target list or the view id is in the
700     * targetId list. If the target parameter is null, then the target list
701     * is not checked (this is in the case of ListView items, where the
702     * views are ignored and only the ids are used).
703     */
704    boolean isValidTarget(View target) {
705        int targetId = target.getId();
706        if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) {
707            return false;
708        }
709        if (mTargetExcludes != null && mTargetExcludes.contains(target)) {
710            return false;
711        }
712        if (mTargetTypeExcludes != null && target != null) {
713            int numTypes = mTargetTypeExcludes.size();
714            for (int i = 0; i < numTypes; ++i) {
715                Class type = mTargetTypeExcludes.get(i);
716                if (type.isInstance(target)) {
717                    return false;
718                }
719            }
720        }
721        if (mTargetNameExcludes != null && target != null && target.getViewName() != null) {
722            if (mTargetNameExcludes.contains(target.getViewName())) {
723                return false;
724            }
725        }
726        if (mTargetIds.size() == 0 && mTargets.size() == 0 &&
727                (mTargetTypes == null || mTargetTypes.isEmpty() &&
728                (mTargetNames == null || mTargetNames.isEmpty()))) {
729            return true;
730        }
731        if (mTargetIds.contains(targetId) || mTargets.contains(target)) {
732            return true;
733        }
734        if (mTargetNames != null && mTargetNames.contains(target.getViewName())) {
735            return true;
736        }
737        if (mTargetTypes != null) {
738            for (int i = 0; i < mTargetTypes.size(); ++i) {
739                if (mTargetTypes.get(i).isInstance(target)) {
740                    return true;
741                }
742            }
743        }
744        return false;
745    }
746
747    private static ArrayMap<Animator, AnimationInfo> getRunningAnimators() {
748        ArrayMap<Animator, AnimationInfo> runningAnimators = sRunningAnimators.get();
749        if (runningAnimators == null) {
750            runningAnimators = new ArrayMap<Animator, AnimationInfo>();
751            sRunningAnimators.set(runningAnimators);
752        }
753        return runningAnimators;
754    }
755
756    /**
757     * This is called internally once all animations have been set up by the
758     * transition hierarchy.
759     *
760     * @hide
761     */
762    protected void runAnimators() {
763        if (DBG) {
764            Log.d(LOG_TAG, "runAnimators() on " + this);
765        }
766        start();
767        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
768        // Now start every Animator that was previously created for this transition
769        for (Animator anim : mAnimators) {
770            if (DBG) {
771                Log.d(LOG_TAG, "  anim: " + anim);
772            }
773            if (runningAnimators.containsKey(anim)) {
774                start();
775                runAnimator(anim, runningAnimators);
776            }
777        }
778        mAnimators.clear();
779        end();
780    }
781
782    private void runAnimator(Animator animator,
783            final ArrayMap<Animator, AnimationInfo> runningAnimators) {
784        if (animator != null) {
785            // TODO: could be a single listener instance for all of them since it uses the param
786            animator.addListener(new AnimatorListenerAdapter() {
787                @Override
788                public void onAnimationStart(Animator animation) {
789                    mCurrentAnimators.add(animation);
790                }
791                @Override
792                public void onAnimationEnd(Animator animation) {
793                    runningAnimators.remove(animation);
794                    mCurrentAnimators.remove(animation);
795                }
796            });
797            animate(animator);
798        }
799    }
800
801    /**
802     * Captures the values in the start scene for the properties that this
803     * transition monitors. These values are then passed as the startValues
804     * structure in a later call to
805     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
806     * The main concern for an implementation is what the
807     * properties are that the transition cares about and what the values are
808     * for all of those properties. The start and end values will be compared
809     * later during the
810     * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}
811     * method to determine what, if any, animations, should be run.
812     *
813     * <p>Subclasses must implement this method. The method should only be called by the
814     * transition system; it is not intended to be called from external classes.</p>
815     *
816     * @param transitionValues The holder for any values that the Transition
817     * wishes to store. Values are stored in the <code>values</code> field
818     * of this TransitionValues object and are keyed from
819     * a String value. For example, to store a view's rotation value,
820     * a transition might call
821     * <code>transitionValues.values.put("appname:transitionname:rotation",
822     * view.getRotation())</code>. The target view will already be stored in
823     * the transitionValues structure when this method is called.
824     *
825     * @see #captureEndValues(TransitionValues)
826     * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
827     */
828    public abstract void captureStartValues(TransitionValues transitionValues);
829
830    /**
831     * Captures the values in the end scene for the properties that this
832     * transition monitors. These values are then passed as the endValues
833     * structure in a later call to
834     * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
835     * The main concern for an implementation is what the
836     * properties are that the transition cares about and what the values are
837     * for all of those properties. The start and end values will be compared
838     * later during the
839     * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}
840     * method to determine what, if any, animations, should be run.
841     *
842     * <p>Subclasses must implement this method. The method should only be called by the
843     * transition system; it is not intended to be called from external classes.</p>
844     *
845     * @param transitionValues The holder for any values that the Transition
846     * wishes to store. Values are stored in the <code>values</code> field
847     * of this TransitionValues object and are keyed from
848     * a String value. For example, to store a view's rotation value,
849     * a transition might call
850     * <code>transitionValues.values.put("appname:transitionname:rotation",
851     * view.getRotation())</code>. The target view will already be stored in
852     * the transitionValues structure when this method is called.
853     *
854     * @see #captureStartValues(TransitionValues)
855     * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
856     */
857    public abstract void captureEndValues(TransitionValues transitionValues);
858
859    /**
860     * Adds the id of a target view that this Transition is interested in
861     * animating. By default, there are no targetIds, and a Transition will
862     * listen for changes on every view in the hierarchy below the sceneRoot
863     * of the Scene being transitioned into. Setting targetIds constrains
864     * the Transition to only listen for, and act on, views with these IDs.
865     * Views with different IDs, or no IDs whatsoever, will be ignored.
866     *
867     * <p>Note that using ids to specify targets implies that ids should be unique
868     * within the view hierarchy underneat the scene root.</p>
869     *
870     * @see View#getId()
871     * @param targetId The id of a target view, must be a positive number.
872     * @return The Transition to which the targetId is added.
873     * Returning the same object makes it easier to chain calls during
874     * construction, such as
875     * <code>transitionSet.addTransitions(new Fade()).addTarget(someId);</code>
876     */
877    public Transition addTarget(int targetId) {
878        if (targetId > 0) {
879            mTargetIds.add(targetId);
880        }
881        return this;
882    }
883
884    /**
885     * Adds the viewName of a target view that this Transition is interested in
886     * animating. By default, there are no targetNames, and a Transition will
887     * listen for changes on every view in the hierarchy below the sceneRoot
888     * of the Scene being transitioned into. Setting targetNames constrains
889     * the Transition to only listen for, and act on, views with these viewNames.
890     * Views with different viewNames, or no viewName whatsoever, will be ignored.
891     *
892     * <p>Note that viewNames should be unique within the view hierarchy.</p>
893     *
894     * @see android.view.View#getViewName()
895     * @param targetName The viewName of a target view, must be non-null.
896     * @return The Transition to which the target viewName is added.
897     * Returning the same object makes it easier to chain calls during
898     * construction, such as
899     * <code>transitionSet.addTransitions(new Fade()).addTarget(someName);</code>
900     */
901    public Transition addTarget(String targetName) {
902        if (targetName != null) {
903            if (mTargetNames != null) {
904                mTargetNames = new ArrayList<String>();
905            }
906            mTargetNames.add(targetName);
907        }
908        return this;
909    }
910
911    /**
912     * Adds the Class of a target view that this Transition is interested in
913     * animating. By default, there are no targetTypes, and a Transition will
914     * listen for changes on every view in the hierarchy below the sceneRoot
915     * of the Scene being transitioned into. Setting targetTypes constrains
916     * the Transition to only listen for, and act on, views with these classes.
917     * Views with different classes will be ignored.
918     *
919     * <p>Note that any View that can be cast to targetType will be included, so
920     * if targetType is <code>View.class</code>, all Views will be included.</p>
921     *
922     * @see #addTarget(int)
923     * @see #addTarget(android.view.View)
924     * @see #excludeTarget(Class, boolean)
925     * @see #excludeChildren(Class, boolean)
926     *
927     * @param targetType The type to include when running this transition.
928     * @return The Transition to which the target class was added.
929     * Returning the same object makes it easier to chain calls during
930     * construction, such as
931     * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code>
932     */
933    public Transition addTarget(Class targetType) {
934        if (targetType != null) {
935            if (mTargetTypes == null) {
936                mTargetTypes = new ArrayList<Class>();
937            }
938            mTargetTypes.add(targetType);
939        }
940        return this;
941    }
942
943    /**
944     * Removes the given targetId from the list of ids that this Transition
945     * is interested in animating.
946     *
947     * @param targetId The id of a target view, must be a positive number.
948     * @return The Transition from which the targetId is removed.
949     * Returning the same object makes it easier to chain calls during
950     * construction, such as
951     * <code>transitionSet.addTransitions(new Fade()).removeTargetId(someId);</code>
952     */
953    public Transition removeTarget(int targetId) {
954        if (targetId > 0) {
955            mTargetIds.remove(targetId);
956        }
957        return this;
958    }
959
960    /**
961     * Removes the given targetName from the list of viewNames that this Transition
962     * is interested in animating.
963     *
964     * @param targetName The viewName of a target view, must not be null.
965     * @return The Transition from which the targetName is removed.
966     * Returning the same object makes it easier to chain calls during
967     * construction, such as
968     * <code>transitionSet.addTransitions(new Fade()).removeTargetName(someName);</code>
969     */
970    public Transition removeTarget(String targetName) {
971        if (targetName != null && mTargetNames != null) {
972            mTargetNames.remove(targetName);
973        }
974        return this;
975    }
976
977    /**
978     * Whether to add the given id to the list of target ids to exclude from this
979     * transition. The <code>exclude</code> parameter specifies whether the target
980     * should be added to or removed from the excluded list.
981     *
982     * <p>Excluding targets is a general mechanism for allowing transitions to run on
983     * a view hierarchy while skipping target views that should not be part of
984     * the transition. For example, you may want to avoid animating children
985     * of a specific ListView or Spinner. Views can be excluded either by their
986     * id, or by their instance reference, or by the Class of that view
987     * (eg, {@link Spinner}).</p>
988     *
989     * @see #excludeChildren(int, boolean)
990     * @see #excludeTarget(View, boolean)
991     * @see #excludeTarget(Class, boolean)
992     *
993     * @param targetId The id of a target to ignore when running this transition.
994     * @param exclude Whether to add the target to or remove the target from the
995     * current list of excluded targets.
996     * @return This transition object.
997     */
998    public Transition excludeTarget(int targetId, boolean exclude) {
999        if (targetId >= 0) {
1000            mTargetIdExcludes = excludeObject(mTargetIdExcludes, targetId, exclude);
1001        }
1002        return this;
1003    }
1004
1005    /**
1006     * Whether to add the given viewName to the list of target viewNames to exclude from this
1007     * transition. The <code>exclude</code> parameter specifies whether the target
1008     * should be added to or removed from the excluded list.
1009     *
1010     * <p>Excluding targets is a general mechanism for allowing transitions to run on
1011     * a view hierarchy while skipping target views that should not be part of
1012     * the transition. For example, you may want to avoid animating children
1013     * of a specific ListView or Spinner. Views can be excluded by their
1014     * id, their instance reference, their viewName, or by the Class of that view
1015     * (eg, {@link Spinner}).</p>
1016     *
1017     * @see #excludeTarget(View, boolean)
1018     * @see #excludeTarget(int, boolean)
1019     * @see #excludeTarget(Class, boolean)
1020     *
1021     * @param targetViewName The name of a target to ignore when running this transition.
1022     * @param exclude Whether to add the target to or remove the target from the
1023     * current list of excluded targets.
1024     * @return This transition object.
1025     */
1026    public Transition excludeTarget(String targetViewName, boolean exclude) {
1027        mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetViewName, exclude);
1028        return this;
1029    }
1030
1031    /**
1032     * Whether to add the children of the given id to the list of targets to exclude
1033     * from this transition. The <code>exclude</code> parameter specifies whether
1034     * the children of the target should be added to or removed from the excluded list.
1035     * Excluding children in this way provides a simple mechanism for excluding all
1036     * children of specific targets, rather than individually excluding each
1037     * child individually.
1038     *
1039     * <p>Excluding targets is a general mechanism for allowing transitions to run on
1040     * a view hierarchy while skipping target views that should not be part of
1041     * the transition. For example, you may want to avoid animating children
1042     * of a specific ListView or Spinner. Views can be excluded either by their
1043     * id, or by their instance reference, or by the Class of that view
1044     * (eg, {@link Spinner}).</p>
1045     *
1046     * @see #excludeTarget(int, boolean)
1047     * @see #excludeChildren(View, boolean)
1048     * @see #excludeChildren(Class, boolean)
1049     *
1050     * @param targetId The id of a target whose children should be ignored when running
1051     * this transition.
1052     * @param exclude Whether to add the target to or remove the target from the
1053     * current list of excluded-child targets.
1054     * @return This transition object.
1055     */
1056    public Transition excludeChildren(int targetId, boolean exclude) {
1057        if (targetId >= 0) {
1058            mTargetIdChildExcludes = excludeObject(mTargetIdChildExcludes, targetId, exclude);
1059        }
1060        return this;
1061    }
1062
1063    /**
1064     * Whether to add the given target to the list of targets to exclude from this
1065     * transition. The <code>exclude</code> parameter specifies whether the target
1066     * should be added to or removed from the excluded list.
1067     *
1068     * <p>Excluding targets is a general mechanism for allowing transitions to run on
1069     * a view hierarchy while skipping target views that should not be part of
1070     * the transition. For example, you may want to avoid animating children
1071     * of a specific ListView or Spinner. Views can be excluded either by their
1072     * id, or by their instance reference, or by the Class of that view
1073     * (eg, {@link Spinner}).</p>
1074     *
1075     * @see #excludeChildren(View, boolean)
1076     * @see #excludeTarget(int, boolean)
1077     * @see #excludeTarget(Class, boolean)
1078     *
1079     * @param target The target to ignore when running this transition.
1080     * @param exclude Whether to add the target to or remove the target from the
1081     * current list of excluded targets.
1082     * @return This transition object.
1083     */
1084    public Transition excludeTarget(View target, boolean exclude) {
1085        mTargetExcludes = excludeObject(mTargetExcludes, target, exclude);
1086        return this;
1087    }
1088
1089    /**
1090     * Whether to add the children of given target to the list of target children
1091     * to exclude from this transition. The <code>exclude</code> parameter specifies
1092     * whether the target should be added to or removed from the excluded list.
1093     *
1094     * <p>Excluding targets is a general mechanism for allowing transitions to run on
1095     * a view hierarchy while skipping target views that should not be part of
1096     * the transition. For example, you may want to avoid animating children
1097     * of a specific ListView or Spinner. Views can be excluded either by their
1098     * id, or by their instance reference, or by the Class of that view
1099     * (eg, {@link Spinner}).</p>
1100     *
1101     * @see #excludeTarget(View, boolean)
1102     * @see #excludeChildren(int, boolean)
1103     * @see #excludeChildren(Class, boolean)
1104     *
1105     * @param target The target to ignore when running this transition.
1106     * @param exclude Whether to add the target to or remove the target from the
1107     * current list of excluded targets.
1108     * @return This transition object.
1109     */
1110    public Transition excludeChildren(View target, boolean exclude) {
1111        mTargetChildExcludes = excludeObject(mTargetChildExcludes, target, exclude);
1112        return this;
1113    }
1114
1115    /**
1116     * Utility method to manage the boilerplate code that is the same whether we
1117     * are excluding targets or their children.
1118     */
1119    private static <T> ArrayList<T> excludeObject(ArrayList<T> list, T target, boolean exclude) {
1120        if (target != null) {
1121            if (exclude) {
1122                list = ArrayListManager.add(list, target);
1123            } else {
1124                list = ArrayListManager.remove(list, target);
1125            }
1126        }
1127        return list;
1128    }
1129
1130    /**
1131     * Whether to add the given type to the list of types to exclude from this
1132     * transition. The <code>exclude</code> parameter specifies whether the target
1133     * type should be added to or removed from the excluded list.
1134     *
1135     * <p>Excluding targets is a general mechanism for allowing transitions to run on
1136     * a view hierarchy while skipping target views that should not be part of
1137     * the transition. For example, you may want to avoid animating children
1138     * of a specific ListView or Spinner. Views can be excluded either by their
1139     * id, or by their instance reference, or by the Class of that view
1140     * (eg, {@link Spinner}).</p>
1141     *
1142     * @see #excludeChildren(Class, boolean)
1143     * @see #excludeTarget(int, boolean)
1144     * @see #excludeTarget(View, boolean)
1145     *
1146     * @param type The type to ignore when running this transition.
1147     * @param exclude Whether to add the target type to or remove it from the
1148     * current list of excluded target types.
1149     * @return This transition object.
1150     */
1151    public Transition excludeTarget(Class type, boolean exclude) {
1152        mTargetTypeExcludes = excludeObject(mTargetTypeExcludes, type, exclude);
1153        return this;
1154    }
1155
1156    /**
1157     * Whether to add the given type to the list of types whose children should
1158     * be excluded from this transition. The <code>exclude</code> parameter
1159     * specifies whether the target type should be added to or removed from
1160     * the excluded list.
1161     *
1162     * <p>Excluding targets is a general mechanism for allowing transitions to run on
1163     * a view hierarchy while skipping target views that should not be part of
1164     * the transition. For example, you may want to avoid animating children
1165     * of a specific ListView or Spinner. Views can be excluded either by their
1166     * id, or by their instance reference, or by the Class of that view
1167     * (eg, {@link Spinner}).</p>
1168     *
1169     * @see #excludeTarget(Class, boolean)
1170     * @see #excludeChildren(int, boolean)
1171     * @see #excludeChildren(View, boolean)
1172     *
1173     * @param type The type to ignore when running this transition.
1174     * @param exclude Whether to add the target type to or remove it from the
1175     * current list of excluded target types.
1176     * @return This transition object.
1177     */
1178    public Transition excludeChildren(Class type, boolean exclude) {
1179        mTargetTypeChildExcludes = excludeObject(mTargetTypeChildExcludes, type, exclude);
1180        return this;
1181    }
1182
1183    /**
1184     * Sets the target view instances that this Transition is interested in
1185     * animating. By default, there are no targets, and a Transition will
1186     * listen for changes on every view in the hierarchy below the sceneRoot
1187     * of the Scene being transitioned into. Setting targets constrains
1188     * the Transition to only listen for, and act on, these views.
1189     * All other views will be ignored.
1190     *
1191     * <p>The target list is like the {@link #addTarget(int) targetId}
1192     * list except this list specifies the actual View instances, not the ids
1193     * of the views. This is an important distinction when scene changes involve
1194     * view hierarchies which have been inflated separately; different views may
1195     * share the same id but not actually be the same instance. If the transition
1196     * should treat those views as the same, then {@link #addTarget(int)} should be used
1197     * instead of {@link #addTarget(View)}. If, on the other hand, scene changes involve
1198     * changes all within the same view hierarchy, among views which do not
1199     * necessarily have ids set on them, then the target list of views may be more
1200     * convenient.</p>
1201     *
1202     * @see #addTarget(int)
1203     * @param target A View on which the Transition will act, must be non-null.
1204     * @return The Transition to which the target is added.
1205     * Returning the same object makes it easier to chain calls during
1206     * construction, such as
1207     * <code>transitionSet.addTransitions(new Fade()).addTarget(someView);</code>
1208     */
1209    public Transition addTarget(View target) {
1210        mTargets.add(target);
1211        return this;
1212    }
1213
1214    /**
1215     * Removes the given target from the list of targets that this Transition
1216     * is interested in animating.
1217     *
1218     * @param target The target view, must be non-null.
1219     * @return Transition The Transition from which the target is removed.
1220     * Returning the same object makes it easier to chain calls during
1221     * construction, such as
1222     * <code>transitionSet.addTransitions(new Fade()).removeTarget(someView);</code>
1223     */
1224    public Transition removeTarget(View target) {
1225        if (target != null) {
1226            mTargets.remove(target);
1227        }
1228        return this;
1229    }
1230
1231    /**
1232     * Removes the given target from the list of targets that this Transition
1233     * is interested in animating.
1234     *
1235     * @param target The type of the target view, must be non-null.
1236     * @return Transition The Transition from which the target is removed.
1237     * Returning the same object makes it easier to chain calls during
1238     * construction, such as
1239     * <code>transitionSet.addTransitions(new Fade()).removeTarget(someType);</code>
1240     */
1241    public Transition removeTarget(Class target) {
1242        if (target != null) {
1243            mTargetTypes.remove(target);
1244        }
1245        return this;
1246    }
1247
1248    /**
1249     * Returns the list of target IDs that this transition limits itself to
1250     * tracking and animating. If the list is null or empty for
1251     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
1252     * {@link #getTargetTypes()} then this transition is
1253     * not limited to specific views, and will handle changes to any views
1254     * in the hierarchy of a scene change.
1255     *
1256     * @return the list of target IDs
1257     */
1258    public List<Integer> getTargetIds() {
1259        return mTargetIds;
1260    }
1261
1262    /**
1263     * Returns the list of target views that this transition limits itself to
1264     * tracking and animating. If the list is null or empty for
1265     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
1266     * {@link #getTargetTypes()} then this transition is
1267     * not limited to specific views, and will handle changes to any views
1268     * in the hierarchy of a scene change.
1269     *
1270     * @return the list of target views
1271     */
1272    public List<View> getTargets() {
1273        return mTargets;
1274    }
1275
1276    /**
1277     * Returns the list of target viewNames that this transition limits itself to
1278     * tracking and animating. If the list is null or empty for
1279     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
1280     * {@link #getTargetTypes()} then this transition is
1281     * not limited to specific views, and will handle changes to any views
1282     * in the hierarchy of a scene change.
1283     *
1284     * @return the list of target viewNames
1285     */
1286    public List<String> getTargetViewNames() {
1287        return mTargetNames;
1288    }
1289
1290    /**
1291     * Returns the list of target viewNames that this transition limits itself to
1292     * tracking and animating. If the list is null or empty for
1293     * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetViewNames()}, and
1294     * {@link #getTargetTypes()} then this transition is
1295     * not limited to specific views, and will handle changes to any views
1296     * in the hierarchy of a scene change.
1297     *
1298     * @return the list of target Types
1299     */
1300    public List<Class> getTargetTypes() {
1301        return mTargetTypes;
1302    }
1303
1304    /**
1305     * Recursive method that captures values for the given view and the
1306     * hierarchy underneath it.
1307     * @param sceneRoot The root of the view hierarchy being captured
1308     * @param start true if this capture is happening before the scene change,
1309     * false otherwise
1310     */
1311    void captureValues(ViewGroup sceneRoot, boolean start) {
1312        clearValues(start);
1313        if ((mTargetIds.size() > 0 || mTargets.size() > 0)
1314                && (mTargetNames == null || mTargetNames.isEmpty())
1315                && (mTargetTypes == null || mTargetTypes.isEmpty())) {
1316            for (int i = 0; i < mTargetIds.size(); ++i) {
1317                int id = mTargetIds.get(i);
1318                View view = sceneRoot.findViewById(id);
1319                if (view != null) {
1320                    TransitionValues values = new TransitionValues();
1321                    values.view = view;
1322                    if (start) {
1323                        captureStartValues(values);
1324                    } else {
1325                        captureEndValues(values);
1326                    }
1327                    capturePropagationValues(values);
1328                    if (start) {
1329                        addViewValues(mStartValues, view, values);
1330                    } else {
1331                        addViewValues(mEndValues, view, values);
1332                    }
1333                }
1334            }
1335            for (int i = 0; i < mTargets.size(); ++i) {
1336                View view = mTargets.get(i);
1337                TransitionValues values = new TransitionValues();
1338                values.view = view;
1339                if (start) {
1340                    captureStartValues(values);
1341                } else {
1342                    captureEndValues(values);
1343                }
1344                capturePropagationValues(values);
1345                if (start) {
1346                    mStartValues.viewValues.put(view, values);
1347                } else {
1348                    mEndValues.viewValues.put(view, values);
1349                }
1350            }
1351        } else {
1352            captureHierarchy(sceneRoot, start);
1353        }
1354        if (!start && mNameOverrides != null) {
1355            int numOverrides = mNameOverrides.size();
1356            ArrayList<View> overriddenViews = new ArrayList<View>(numOverrides);
1357            for (int i = 0; i < numOverrides; i++) {
1358                String fromName = mNameOverrides.keyAt(i);
1359                overriddenViews.add(mStartValues.nameValues.remove(fromName));
1360            }
1361            for (int i = 0; i < numOverrides; i++) {
1362                View view = overriddenViews.get(i);
1363                if (view != null) {
1364                    String toName = mNameOverrides.valueAt(i);
1365                    mStartValues.nameValues.put(toName, view);
1366                }
1367            }
1368        }
1369    }
1370
1371    static void addViewValues(TransitionValuesMaps transitionValuesMaps,
1372            View view, TransitionValues transitionValues) {
1373        transitionValuesMaps.viewValues.put(view, transitionValues);
1374        int id = view.getId();
1375        if (id >= 0) {
1376            if (transitionValuesMaps.idValues.indexOfKey(id) >= 0) {
1377                // Duplicate IDs cannot match by ID.
1378                transitionValuesMaps.idValues.put(id, null);
1379            } else {
1380                transitionValuesMaps.idValues.put(id, view);
1381            }
1382        }
1383        String name = view.getViewName();
1384        if (name != null) {
1385            if (transitionValuesMaps.nameValues.containsKey(name)) {
1386                // Duplicate viewNames: cannot match by viewName.
1387                transitionValuesMaps.nameValues.put(name, null);
1388            } else {
1389                transitionValuesMaps.nameValues.put(name, view);
1390            }
1391        }
1392        if (view.getParent() instanceof ListView) {
1393            ListView listview = (ListView) view.getParent();
1394            if (listview.getAdapter().hasStableIds()) {
1395                int position = listview.getPositionForView(view);
1396                long itemId = listview.getItemIdAtPosition(position);
1397                if (transitionValuesMaps.itemIdValues.indexOfKey(itemId) >= 0) {
1398                    // Duplicate item IDs: cannot match by item ID.
1399                    View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId);
1400                    if (alreadyMatched != null) {
1401                        alreadyMatched.setHasTransientState(false);
1402                        transitionValuesMaps.itemIdValues.put(itemId, null);
1403                    }
1404                } else {
1405                    view.setHasTransientState(true);
1406                    transitionValuesMaps.itemIdValues.put(itemId, view);
1407                }
1408            }
1409        }
1410    }
1411
1412    /**
1413     * Clear valuesMaps for specified start/end state
1414     *
1415     * @param start true if the start values should be cleared, false otherwise
1416     */
1417    void clearValues(boolean start) {
1418        if (start) {
1419            mStartValues.viewValues.clear();
1420            mStartValues.idValues.clear();
1421            mStartValues.itemIdValues.clear();
1422            mStartValues.nameValues.clear();
1423        } else {
1424            mEndValues.viewValues.clear();
1425            mEndValues.idValues.clear();
1426            mEndValues.itemIdValues.clear();
1427            mEndValues.nameValues.clear();
1428        }
1429    }
1430
1431    /**
1432     * Recursive method which captures values for an entire view hierarchy,
1433     * starting at some root view. Transitions without targetIDs will use this
1434     * method to capture values for all possible views.
1435     *
1436     * @param view The view for which to capture values. Children of this View
1437     * will also be captured, recursively down to the leaf nodes.
1438     * @param start true if values are being captured in the start scene, false
1439     * otherwise.
1440     */
1441    private void captureHierarchy(View view, boolean start) {
1442        if (view == null) {
1443            return;
1444        }
1445        int id = view.getId();
1446        if (mTargetIdExcludes != null && mTargetIdExcludes.contains(id)) {
1447            return;
1448        }
1449        if (mTargetExcludes != null && mTargetExcludes.contains(view)) {
1450            return;
1451        }
1452        if (mTargetTypeExcludes != null && view != null) {
1453            int numTypes = mTargetTypeExcludes.size();
1454            for (int i = 0; i < numTypes; ++i) {
1455                if (mTargetTypeExcludes.get(i).isInstance(view)) {
1456                    return;
1457                }
1458            }
1459        }
1460        if (view.getParent() instanceof ViewGroup) {
1461            TransitionValues values = new TransitionValues();
1462            values.view = view;
1463            if (start) {
1464                captureStartValues(values);
1465            } else {
1466                captureEndValues(values);
1467            }
1468            capturePropagationValues(values);
1469            if (start) {
1470                addViewValues(mStartValues, view, values);
1471            } else {
1472                addViewValues(mEndValues, view, values);
1473            }
1474        }
1475        if (view instanceof ViewGroup) {
1476            // Don't traverse child hierarchy if there are any child-excludes on this view
1477            if (mTargetIdChildExcludes != null && mTargetIdChildExcludes.contains(id)) {
1478                return;
1479            }
1480            if (mTargetChildExcludes != null && mTargetChildExcludes.contains(view)) {
1481                return;
1482            }
1483            if (mTargetTypeChildExcludes != null) {
1484                int numTypes = mTargetTypeChildExcludes.size();
1485                for (int i = 0; i < numTypes; ++i) {
1486                    if (mTargetTypeChildExcludes.get(i).isInstance(view)) {
1487                        return;
1488                    }
1489                }
1490            }
1491            ViewGroup parent = (ViewGroup) view;
1492            for (int i = 0; i < parent.getChildCount(); ++i) {
1493                captureHierarchy(parent.getChildAt(i), start);
1494            }
1495        }
1496    }
1497
1498    /**
1499     * This method can be called by transitions to get the TransitionValues for
1500     * any particular view during the transition-playing process. This might be
1501     * necessary, for example, to query the before/after state of related views
1502     * for a given transition.
1503     */
1504    public TransitionValues getTransitionValues(View view, boolean start) {
1505        if (mParent != null) {
1506            return mParent.getTransitionValues(view, start);
1507        }
1508        TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues;
1509        return valuesMaps.viewValues.get(view);
1510    }
1511
1512    /**
1513     * Pauses this transition, sending out calls to {@link
1514     * TransitionListener#onTransitionPause(Transition)} to all listeners
1515     * and pausing all running animators started by this transition.
1516     *
1517     * @hide
1518     */
1519    public void pause(View sceneRoot) {
1520        if (!mEnded) {
1521            ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
1522            int numOldAnims = runningAnimators.size();
1523            WindowId windowId = sceneRoot.getWindowId();
1524            for (int i = numOldAnims - 1; i >= 0; i--) {
1525                AnimationInfo info = runningAnimators.valueAt(i);
1526                if (info.view != null && windowId.equals(info.windowId)) {
1527                    Animator anim = runningAnimators.keyAt(i);
1528                    anim.pause();
1529                }
1530            }
1531            if (mListeners != null && mListeners.size() > 0) {
1532                ArrayList<TransitionListener> tmpListeners =
1533                        (ArrayList<TransitionListener>) mListeners.clone();
1534                int numListeners = tmpListeners.size();
1535                for (int i = 0; i < numListeners; ++i) {
1536                    tmpListeners.get(i).onTransitionPause(this);
1537                }
1538            }
1539            mPaused = true;
1540        }
1541    }
1542
1543    /**
1544     * Resumes this transition, sending out calls to {@link
1545     * TransitionListener#onTransitionPause(Transition)} to all listeners
1546     * and pausing all running animators started by this transition.
1547     *
1548     * @hide
1549     */
1550    public void resume(View sceneRoot) {
1551        if (mPaused) {
1552            if (!mEnded) {
1553                ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
1554                int numOldAnims = runningAnimators.size();
1555                WindowId windowId = sceneRoot.getWindowId();
1556                for (int i = numOldAnims - 1; i >= 0; i--) {
1557                    AnimationInfo info = runningAnimators.valueAt(i);
1558                    if (info.view != null && windowId.equals(info.windowId)) {
1559                        Animator anim = runningAnimators.keyAt(i);
1560                        anim.resume();
1561                    }
1562                }
1563                if (mListeners != null && mListeners.size() > 0) {
1564                    ArrayList<TransitionListener> tmpListeners =
1565                            (ArrayList<TransitionListener>) mListeners.clone();
1566                    int numListeners = tmpListeners.size();
1567                    for (int i = 0; i < numListeners; ++i) {
1568                        tmpListeners.get(i).onTransitionResume(this);
1569                    }
1570                }
1571            }
1572            mPaused = false;
1573        }
1574    }
1575
1576    /**
1577     * Called by TransitionManager to play the transition. This calls
1578     * createAnimators() to set things up and create all of the animations and then
1579     * runAnimations() to actually start the animations.
1580     */
1581    void playTransition(ViewGroup sceneRoot) {
1582        ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
1583        int numOldAnims = runningAnimators.size();
1584        for (int i = numOldAnims - 1; i >= 0; i--) {
1585            Animator anim = runningAnimators.keyAt(i);
1586            if (anim != null) {
1587                AnimationInfo oldInfo = runningAnimators.get(anim);
1588                if (oldInfo != null && oldInfo.view != null &&
1589                        oldInfo.view.getContext() == sceneRoot.getContext()) {
1590                    boolean cancel = false;
1591                    TransitionValues oldValues = oldInfo.values;
1592                    View oldView = oldInfo.view;
1593                    TransitionValues newValues = mEndValues.viewValues.get(oldView);
1594                    if (oldValues != null) {
1595                        // if oldValues null, then transition didn't care to stash values,
1596                        // and won't get canceled
1597                        if (newValues != null) {
1598                            for (String key : oldValues.values.keySet()) {
1599                                Object oldValue = oldValues.values.get(key);
1600                                Object newValue = newValues.values.get(key);
1601                                if (oldValue != null && newValue != null &&
1602                                        !oldValue.equals(newValue)) {
1603                                    cancel = true;
1604                                    if (DBG) {
1605                                        Log.d(LOG_TAG, "Transition.playTransition: " +
1606                                                "oldValue != newValue for " + key +
1607                                                ": old, new = " + oldValue + ", " + newValue);
1608                                    }
1609                                    break;
1610                                }
1611                            }
1612                        }
1613                    }
1614                    if (cancel) {
1615                        if (anim.isRunning() || anim.isStarted()) {
1616                            if (DBG) {
1617                                Log.d(LOG_TAG, "Canceling anim " + anim);
1618                            }
1619                            anim.cancel();
1620                        } else {
1621                            if (DBG) {
1622                                Log.d(LOG_TAG, "removing anim from info list: " + anim);
1623                            }
1624                            runningAnimators.remove(anim);
1625                        }
1626                    }
1627                }
1628            }
1629        }
1630
1631        createAnimators(sceneRoot, mStartValues, mEndValues);
1632        runAnimators();
1633    }
1634
1635    /**
1636     * This is a utility method used by subclasses to handle standard parts of
1637     * setting up and running an Animator: it sets the {@link #getDuration()
1638     * duration} and the {@link #getStartDelay() startDelay}, starts the
1639     * animation, and, when the animator ends, calls {@link #end()}.
1640     *
1641     * @param animator The Animator to be run during this transition.
1642     *
1643     * @hide
1644     */
1645    protected void animate(Animator animator) {
1646        // TODO: maybe pass auto-end as a boolean parameter?
1647        if (animator == null) {
1648            end();
1649        } else {
1650            if (getDuration() >= 0) {
1651                animator.setDuration(getDuration());
1652            }
1653            if (getStartDelay() >= 0) {
1654                animator.setStartDelay(getStartDelay() + animator.getStartDelay());
1655            }
1656            if (getInterpolator() != null) {
1657                animator.setInterpolator(getInterpolator());
1658            }
1659            animator.addListener(new AnimatorListenerAdapter() {
1660                @Override
1661                public void onAnimationEnd(Animator animation) {
1662                    end();
1663                    animation.removeListener(this);
1664                }
1665            });
1666            animator.start();
1667        }
1668    }
1669
1670    /**
1671     * This method is called automatically by the transition and
1672     * TransitionSet classes prior to a Transition subclass starting;
1673     * subclasses should not need to call it directly.
1674     *
1675     * @hide
1676     */
1677    protected void start() {
1678        if (mNumInstances == 0) {
1679            if (mListeners != null && mListeners.size() > 0) {
1680                ArrayList<TransitionListener> tmpListeners =
1681                        (ArrayList<TransitionListener>) mListeners.clone();
1682                int numListeners = tmpListeners.size();
1683                for (int i = 0; i < numListeners; ++i) {
1684                    tmpListeners.get(i).onTransitionStart(this);
1685                }
1686            }
1687            mEnded = false;
1688        }
1689        mNumInstances++;
1690    }
1691
1692    /**
1693     * This method is called automatically by the Transition and
1694     * TransitionSet classes when a transition finishes, either because
1695     * a transition did nothing (returned a null Animator from
1696     * {@link Transition#createAnimator(ViewGroup, TransitionValues,
1697     * TransitionValues)}) or because the transition returned a valid
1698     * Animator and end() was called in the onAnimationEnd()
1699     * callback of the AnimatorListener.
1700     *
1701     * @hide
1702     */
1703    protected void end() {
1704        --mNumInstances;
1705        if (mNumInstances == 0) {
1706            if (mListeners != null && mListeners.size() > 0) {
1707                ArrayList<TransitionListener> tmpListeners =
1708                        (ArrayList<TransitionListener>) mListeners.clone();
1709                int numListeners = tmpListeners.size();
1710                for (int i = 0; i < numListeners; ++i) {
1711                    tmpListeners.get(i).onTransitionEnd(this);
1712                }
1713            }
1714            for (int i = 0; i < mStartValues.itemIdValues.size(); ++i) {
1715                View view = mStartValues.itemIdValues.valueAt(i);
1716                if (view != null) {
1717                    view.setHasTransientState(false);
1718                }
1719            }
1720            for (int i = 0; i < mEndValues.itemIdValues.size(); ++i) {
1721                View view = mEndValues.itemIdValues.valueAt(i);
1722                if (view != null) {
1723                    view.setHasTransientState(false);
1724                }
1725            }
1726            mEnded = true;
1727        }
1728    }
1729
1730    /**
1731     * This method cancels a transition that is currently running.
1732     *
1733     * @hide
1734     */
1735    protected void cancel() {
1736        int numAnimators = mCurrentAnimators.size();
1737        for (int i = numAnimators - 1; i >= 0; i--) {
1738            Animator animator = mCurrentAnimators.get(i);
1739            animator.cancel();
1740        }
1741        if (mListeners != null && mListeners.size() > 0) {
1742            ArrayList<TransitionListener> tmpListeners =
1743                    (ArrayList<TransitionListener>) mListeners.clone();
1744            int numListeners = tmpListeners.size();
1745            for (int i = 0; i < numListeners; ++i) {
1746                tmpListeners.get(i).onTransitionCancel(this);
1747            }
1748        }
1749    }
1750
1751    /**
1752     * Adds a listener to the set of listeners that are sent events through the
1753     * life of an animation, such as start, repeat, and end.
1754     *
1755     * @param listener the listener to be added to the current set of listeners
1756     * for this animation.
1757     * @return This transition object.
1758     */
1759    public Transition addListener(TransitionListener listener) {
1760        if (mListeners == null) {
1761            mListeners = new ArrayList<TransitionListener>();
1762        }
1763        mListeners.add(listener);
1764        return this;
1765    }
1766
1767    /**
1768     * Removes a listener from the set listening to this animation.
1769     *
1770     * @param listener the listener to be removed from the current set of
1771     * listeners for this transition.
1772     * @return This transition object.
1773     */
1774    public Transition removeListener(TransitionListener listener) {
1775        if (mListeners == null) {
1776            return this;
1777        }
1778        mListeners.remove(listener);
1779        if (mListeners.size() == 0) {
1780            mListeners = null;
1781        }
1782        return this;
1783    }
1784
1785    /**
1786     * Sets the callback to use to find the epicenter of a Transition. A null value indicates
1787     * that there is no epicenter in the Transition and onGetEpicenter() will return null.
1788     * Transitions like {@link android.transition.Explode} use a point or Rect to orient
1789     * the direction of travel. This is called the epicenter of the Transition and is
1790     * typically centered on a touched View. The
1791     * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
1792     * dynamically retrieve the epicenter during a Transition.
1793     * @param epicenterCallback The callback to use to find the epicenter of the Transition.
1794     */
1795    public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
1796        mEpicenterCallback = epicenterCallback;
1797    }
1798
1799    /**
1800     * Returns the callback used to find the epicenter of the Transition.
1801     * Transitions like {@link android.transition.Explode} use a point or Rect to orient
1802     * the direction of travel. This is called the epicenter of the Transition and is
1803     * typically centered on a touched View. The
1804     * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
1805     * dynamically retrieve the epicenter during a Transition.
1806     * @return the callback used to find the epicenter of the Transition.
1807     */
1808    public EpicenterCallback getEpicenterCallback() {
1809        return mEpicenterCallback;
1810    }
1811
1812    /**
1813     * Returns the epicenter as specified by the
1814     * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
1815     * @return the epicenter as specified by the
1816     * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
1817     * @see #setEpicenterCallback(android.transition.Transition.EpicenterCallback)
1818     */
1819    public Rect getEpicenter() {
1820        if (mEpicenterCallback == null) {
1821            return null;
1822        }
1823        return mEpicenterCallback.onGetEpicenter(this);
1824    }
1825
1826    /**
1827     * Sets the method for determining Animator start delays.
1828     * When a Transition affects several Views like {@link android.transition.Explode} or
1829     * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
1830     * such that the Animator start delay depends on position of the View. The
1831     * TransitionPropagation specifies how the start delays are calculated.
1832     * @param transitionPropagation The class used to determine the start delay of
1833     *                              Animators created by this Transition. A null value
1834     *                              indicates that no delay should be used.
1835     */
1836    public void setPropagation(TransitionPropagation transitionPropagation) {
1837        mPropagation = transitionPropagation;
1838    }
1839
1840    /**
1841     * Returns the {@link android.transition.TransitionPropagation} used to calculate Animator start
1842     * delays.
1843     * When a Transition affects several Views like {@link android.transition.Explode} or
1844     * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
1845     * such that the Animator start delay depends on position of the View. The
1846     * TransitionPropagation specifies how the start delays are calculated.
1847     * @return the {@link android.transition.TransitionPropagation} used to calculate Animator start
1848     * delays. This is null by default.
1849     */
1850    public TransitionPropagation getPropagation() {
1851        return mPropagation;
1852    }
1853
1854    /**
1855     * Captures TransitionPropagation values for the given view and the
1856     * hierarchy underneath it.
1857     */
1858    void capturePropagationValues(TransitionValues transitionValues) {
1859        if (mPropagation != null && !transitionValues.values.isEmpty()) {
1860            String[] propertyNames = mPropagation.getPropagationProperties();
1861            if (propertyNames == null) {
1862                return;
1863            }
1864            boolean containsAll = true;
1865            for (int i = 0; i < propertyNames.length; i++) {
1866                if (!transitionValues.values.containsKey(propertyNames[i])) {
1867                    containsAll = false;
1868                    break;
1869                }
1870            }
1871            if (!containsAll) {
1872                mPropagation.captureValues(transitionValues);
1873            }
1874        }
1875    }
1876
1877    Transition setSceneRoot(ViewGroup sceneRoot) {
1878        mSceneRoot = sceneRoot;
1879        return this;
1880    }
1881
1882    void setCanRemoveViews(boolean canRemoveViews) {
1883        mCanRemoveViews = canRemoveViews;
1884    }
1885
1886    public boolean canRemoveViews() {
1887        return mCanRemoveViews;
1888    }
1889
1890    /**
1891     * Sets the shared element names -- a mapping from a name at the start state to
1892     * a different name at the end state.
1893     * @hide
1894     */
1895    public void setNameOverrides(ArrayMap<String, String> overrides) {
1896        mNameOverrides = overrides;
1897    }
1898
1899    /** @hide */
1900    public ArrayMap<String, String> getNameOverrides() {
1901        return mNameOverrides;
1902    }
1903
1904    @Override
1905    public String toString() {
1906        return toString("");
1907    }
1908
1909    @Override
1910    public Transition clone() {
1911        Transition clone = null;
1912        try {
1913            clone = (Transition) super.clone();
1914            clone.mAnimators = new ArrayList<Animator>();
1915            clone.mStartValues = new TransitionValuesMaps();
1916            clone.mEndValues = new TransitionValuesMaps();
1917        } catch (CloneNotSupportedException e) {}
1918
1919        return clone;
1920    }
1921
1922    /**
1923     * Returns the name of this Transition. This name is used internally to distinguish
1924     * between different transitions to determine when interrupting transitions overlap.
1925     * For example, a ChangeBounds running on the same target view as another ChangeBounds
1926     * should determine whether the old transition is animating to different end values
1927     * and should be canceled in favor of the new transition.
1928     *
1929     * <p>By default, a Transition's name is simply the value of {@link Class#getName()},
1930     * but subclasses are free to override and return something different.</p>
1931     *
1932     * @return The name of this transition.
1933     */
1934    public String getName() {
1935        return mName;
1936    }
1937
1938    String toString(String indent) {
1939        String result = indent + getClass().getSimpleName() + "@" +
1940                Integer.toHexString(hashCode()) + ": ";
1941        if (mDuration != -1) {
1942            result += "dur(" + mDuration + ") ";
1943        }
1944        if (mStartDelay != -1) {
1945            result += "dly(" + mStartDelay + ") ";
1946        }
1947        if (mInterpolator != null) {
1948            result += "interp(" + mInterpolator + ") ";
1949        }
1950        if (mTargetIds.size() > 0 || mTargets.size() > 0) {
1951            result += "tgts(";
1952            if (mTargetIds.size() > 0) {
1953                for (int i = 0; i < mTargetIds.size(); ++i) {
1954                    if (i > 0) {
1955                        result += ", ";
1956                    }
1957                    result += mTargetIds.get(i);
1958                }
1959            }
1960            if (mTargets.size() > 0) {
1961                for (int i = 0; i < mTargets.size(); ++i) {
1962                    if (i > 0) {
1963                        result += ", ";
1964                    }
1965                    result += mTargets.get(i);
1966                }
1967            }
1968            result += ")";
1969        }
1970        return result;
1971    }
1972
1973    /**
1974     * A transition listener receives notifications from a transition.
1975     * Notifications indicate transition lifecycle events.
1976     */
1977    public static interface TransitionListener {
1978        /**
1979         * Notification about the start of the transition.
1980         *
1981         * @param transition The started transition.
1982         */
1983        void onTransitionStart(Transition transition);
1984
1985        /**
1986         * Notification about the end of the transition. Canceled transitions
1987         * will always notify listeners of both the cancellation and end
1988         * events. That is, {@link #onTransitionEnd(Transition)} is always called,
1989         * regardless of whether the transition was canceled or played
1990         * through to completion.
1991         *
1992         * @param transition The transition which reached its end.
1993         */
1994        void onTransitionEnd(Transition transition);
1995
1996        /**
1997         * Notification about the cancellation of the transition.
1998         * Note that cancel may be called by a parent {@link TransitionSet} on
1999         * a child transition which has not yet started. This allows the child
2000         * transition to restore state on target objects which was set at
2001         * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)
2002         * createAnimator()} time.
2003         *
2004         * @param transition The transition which was canceled.
2005         */
2006        void onTransitionCancel(Transition transition);
2007
2008        /**
2009         * Notification when a transition is paused.
2010         * Note that createAnimator() may be called by a parent {@link TransitionSet} on
2011         * a child transition which has not yet started. This allows the child
2012         * transition to restore state on target objects which was set at
2013         * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)
2014         * createAnimator()} time.
2015         *
2016         * @param transition The transition which was paused.
2017         */
2018        void onTransitionPause(Transition transition);
2019
2020        /**
2021         * Notification when a transition is resumed.
2022         * Note that resume() may be called by a parent {@link TransitionSet} on
2023         * a child transition which has not yet started. This allows the child
2024         * transition to restore state which may have changed in an earlier call
2025         * to {@link #onTransitionPause(Transition)}.
2026         *
2027         * @param transition The transition which was resumed.
2028         */
2029        void onTransitionResume(Transition transition);
2030    }
2031
2032    /**
2033     * Utility adapter class to avoid having to override all three methods
2034     * whenever someone just wants to listen for a single event.
2035     *
2036     * @hide
2037     * */
2038    public static class TransitionListenerAdapter implements TransitionListener {
2039        @Override
2040        public void onTransitionStart(Transition transition) {
2041        }
2042
2043        @Override
2044        public void onTransitionEnd(Transition transition) {
2045        }
2046
2047        @Override
2048        public void onTransitionCancel(Transition transition) {
2049        }
2050
2051        @Override
2052        public void onTransitionPause(Transition transition) {
2053        }
2054
2055        @Override
2056        public void onTransitionResume(Transition transition) {
2057        }
2058    }
2059
2060    /**
2061     * Holds information about each animator used when a new transition starts
2062     * while other transitions are still running to determine whether a running
2063     * animation should be canceled or a new animation noop'd. The structure holds
2064     * information about the state that an animation is going to, to be compared to
2065     * end state of a new animation.
2066     * @hide
2067     */
2068    public static class AnimationInfo {
2069        public View view;
2070        String name;
2071        TransitionValues values;
2072        WindowId windowId;
2073
2074        AnimationInfo(View view, String name, WindowId windowId, TransitionValues values) {
2075            this.view = view;
2076            this.name = name;
2077            this.values = values;
2078            this.windowId = windowId;
2079        }
2080    }
2081
2082    /**
2083     * Utility class for managing typed ArrayLists efficiently. In particular, this
2084     * can be useful for lists that we don't expect to be used often (eg, the exclude
2085     * lists), so we'd like to keep them nulled out by default. This causes the code to
2086     * become tedious, with constant null checks, code to allocate when necessary,
2087     * and code to null out the reference when the list is empty. This class encapsulates
2088     * all of that functionality into simple add()/remove() methods which perform the
2089     * necessary checks, allocation/null-out as appropriate, and return the
2090     * resulting list.
2091     */
2092    private static class ArrayListManager {
2093
2094        /**
2095         * Add the specified item to the list, returning the resulting list.
2096         * The returned list can either the be same list passed in or, if that
2097         * list was null, the new list that was created.
2098         *
2099         * Note that the list holds unique items; if the item already exists in the
2100         * list, the list is not modified.
2101         */
2102        static <T> ArrayList<T> add(ArrayList<T> list, T item) {
2103            if (list == null) {
2104                list = new ArrayList<T>();
2105            }
2106            if (!list.contains(item)) {
2107                list.add(item);
2108            }
2109            return list;
2110        }
2111
2112        /**
2113         * Remove the specified item from the list, returning the resulting list.
2114         * The returned list can either the be same list passed in or, if that
2115         * list becomes empty as a result of the remove(), the new list was created.
2116         */
2117        static <T> ArrayList<T> remove(ArrayList<T> list, T item) {
2118            if (list != null) {
2119                list.remove(item);
2120                if (list.isEmpty()) {
2121                    list = null;
2122                }
2123            }
2124            return list;
2125        }
2126    }
2127
2128    /**
2129     * Class to get the epicenter of Transition. Use
2130     * {@link #setEpicenterCallback(android.transition.Transition.EpicenterCallback)} to
2131     * set the callback used to calculate the epicenter of the Transition. Override
2132     * {@link #getEpicenter()} to return the rectangular region in screen coordinates of
2133     * the epicenter of the transition.
2134     * @see #setEpicenterCallback(android.transition.Transition.EpicenterCallback)
2135     */
2136    public static abstract class EpicenterCallback {
2137
2138        /**
2139         * Implementers must override to return the epicenter of the Transition in screen
2140         * coordinates. Transitions like {@link android.transition.Explode} depend upon
2141         * an epicenter for the Transition. In Explode, Views move toward or away from the
2142         * center of the epicenter Rect along the vector between the epicenter and the center
2143         * of the View appearing and disappearing. Some Transitions, such as
2144         * {@link android.transition.Fade} pay no attention to the epicenter.
2145         *
2146         * @param transition The transition for which the epicenter applies.
2147         * @return The Rect region of the epicenter of <code>transition</code> or null if
2148         * there is no epicenter.
2149         */
2150        public abstract Rect onGetEpicenter(Transition transition);
2151    }
2152}
2153