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