blob: 316c7e3f6084408222c6c4ec6132b1af0e23f3f4 [file] [log] [blame]
Chet Haasefaebd8f2012-05-18 14:17:57 -07001/*
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 */
Chet Haase6ebe3de2013-06-17 16:50:50 -070016
Chet Haased82c8ac2013-08-26 14:20:16 -070017package android.transition;
Chet Haasefaebd8f2012-05-18 14:17:57 -070018
19import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.TimeInterpolator;
George Mountaef1ae32015-06-04 12:51:55 -070022import android.annotation.Nullable;
George Mountecd857b2014-06-19 07:51:08 -070023import android.content.Context;
24import android.content.res.TypedArray;
25import android.graphics.Path;
George Mountd6107a32014-03-10 16:51:16 -070026import android.graphics.Rect;
Chet Haase08735182013-06-04 10:44:40 -070027import android.util.ArrayMap;
George Mountecd857b2014-06-19 07:51:08 -070028import android.util.AttributeSet;
Chet Haasec43524f2013-07-16 14:40:11 -070029import android.util.Log;
Chet Haasefaebd8f2012-05-18 14:17:57 -070030import android.util.LongSparseArray;
31import android.util.SparseArray;
George Mountd6107a32014-03-10 16:51:16 -070032import android.util.SparseLongArray;
George Mountecd857b2014-06-19 07:51:08 -070033import android.view.InflateException;
Chet Haasefaebd8f2012-05-18 14:17:57 -070034import android.view.SurfaceView;
35import android.view.TextureView;
36import android.view.View;
37import android.view.ViewGroup;
38import android.view.ViewOverlay;
George Mountcf68aad2014-03-06 14:17:46 -080039import android.view.WindowId;
George Mountecd857b2014-06-19 07:51:08 -070040import android.view.animation.AnimationUtils;
Chet Haasefaebd8f2012-05-18 14:17:57 -070041import android.widget.ListView;
Chet Haaseff58f922013-09-11 13:08:18 -070042import android.widget.Spinner;
Chet Haasefaebd8f2012-05-18 14:17:57 -070043
George Mount23d57f32016-03-08 09:32:07 -080044import com.android.internal.R;
45
Chet Haasefaebd8f2012-05-18 14:17:57 -070046import java.util.ArrayList;
Chet Haased82c8ac2013-08-26 14:20:16 -070047import java.util.List;
George Mountecd857b2014-06-19 07:51:08 -070048import java.util.StringTokenizer;
49
Chet Haasefaebd8f2012-05-18 14:17:57 -070050/**
51 * A Transition holds information about animations that will be run on its
52 * targets during a scene change. Subclasses of this abstract class may
Chet Haased82c8ac2013-08-26 14:20:16 -070053 * choreograph several child transitions ({@link TransitionSet} or they may
Chet Haasefaebd8f2012-05-18 14:17:57 -070054 * perform custom animations themselves. Any Transition has two main jobs:
55 * (1) capture property values, and (2) play animations based on changes to
56 * captured property values. A custom transition knows what property values
57 * on View objects are of interest to it, and also knows how to animate
58 * changes to those values. For example, the {@link Fade} transition tracks
59 * changes to visibility-related properties and is able to construct and run
60 * animations that fade items in or out based on changes to those properties.
61 *
62 * <p>Note: Transitions may not work correctly with either {@link SurfaceView}
63 * or {@link TextureView}, due to the way that these views are displayed
64 * on the screen. For SurfaceView, the problem is that the view is updated from
65 * a non-UI thread, so changes to the view due to transitions (such as moving
66 * and resizing the view) may be out of sync with the display inside those bounds.
67 * TextureView is more compatible with transitions in general, but some
Chet Haased82c8ac2013-08-26 14:20:16 -070068 * specific transitions (such as {@link Fade}) may not be compatible
Chet Haasefaebd8f2012-05-18 14:17:57 -070069 * with TextureView because they rely on {@link ViewOverlay} functionality,
70 * which does not currently work with TextureView.</p>
Chet Haased82c8ac2013-08-26 14:20:16 -070071 *
72 * <p>Transitions can be declared in XML resource files inside the <code>res/transition</code>
73 * directory. Transition resources consist of a tag name for one of the Transition
74 * subclasses along with attributes to define some of the attributes of that transition.
George Mountd6107a32014-03-10 16:51:16 -070075 * For example, here is a minimal resource file that declares a {@link ChangeBounds} transition:
Chet Haased82c8ac2013-08-26 14:20:16 -070076 *
77 * {@sample development/samples/ApiDemos/res/transition/changebounds.xml ChangeBounds}
78 *
George Mount608b87d2014-04-15 09:01:32 -070079 * <p>This TransitionSet contains {@link android.transition.Explode} for visibility,
80 * {@link android.transition.ChangeBounds}, {@link android.transition.ChangeTransform},
George Mount990205e2014-06-24 09:36:18 -070081 * and {@link android.transition.ChangeClipBounds} and
82 * {@link android.transition.ChangeImageTransform}:</p>
George Mountd6107a32014-03-10 16:51:16 -070083 *
George Mount608b87d2014-04-15 09:01:32 -070084 * {@sample development/samples/ApiDemos/res/transition/explode_move_together.xml MultipleTransform}
George Mountd6107a32014-03-10 16:51:16 -070085 *
George Mount44d66382014-06-18 10:56:25 -070086 * <p>Custom transition classes may be instantiated with a <code>transition</code> tag:</p>
87 * <pre>&lt;transition class="my.app.transition.CustomTransition"/></pre>
George Mountb592d842014-10-31 17:19:18 -070088 * <p>Custom transition classes loaded from XML should have a public constructor taking
89 * a {@link android.content.Context} and {@link android.util.AttributeSet}.</p>
George Mount44d66382014-06-18 10:56:25 -070090 *
Chet Haased82c8ac2013-08-26 14:20:16 -070091 * <p>Note that attributes for the transition are not required, just as they are
92 * optional when declared in code; Transitions created from XML resources will use
93 * the same defaults as their code-created equivalents. Here is a slightly more
94 * elaborate example which declares a {@link TransitionSet} transition with
95 * {@link ChangeBounds} and {@link Fade} child transitions:</p>
96 *
97 * {@sample
98 * development/samples/ApiDemos/res/transition/changebounds_fadeout_sequential.xml TransitionSet}
99 *
100 * <p>In this example, the transitionOrdering attribute is used on the TransitionSet
101 * object to change from the default {@link TransitionSet#ORDERING_TOGETHER} behavior
102 * to be {@link TransitionSet#ORDERING_SEQUENTIAL} instead. Also, the {@link Fade}
103 * transition uses a fadingMode of {@link Fade#OUT} instead of the default
104 * out-in behavior. Finally, note the use of the <code>targets</code> sub-tag, which
105 * takes a set of {@link android.R.styleable#TransitionTarget target} tags, each
George Mounta98fb7a2014-04-24 08:57:03 -0700106 * of which lists a specific <code>targetId</code>, <code>targetClass</code>,
George Mount0a2ae002014-06-23 14:57:27 +0000107 * <code>targetName</code>, <code>excludeId</code>, <code>excludeClass</code>, or
108 * <code>excludeName</code>, which this transition acts upon.
Chet Haased82c8ac2013-08-26 14:20:16 -0700109 * Use of targets is optional, but can be used to either limit the time spent checking
110 * attributes on unchanging views, or limiting the types of animations run on specific views.
111 * In this case, we know that only the <code>grayscaleContainer</code> will be
112 * disappearing, so we choose to limit the {@link Fade} transition to only that view.</p>
113 *
114 * Further information on XML resource descriptions for transitions can be found for
115 * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
Dake Guc94e2b32014-07-22 14:53:53 -0700116 * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
117 * {@link android.R.styleable#Slide}, and {@link android.R.styleable#ChangeTransform}.
Chet Haased82c8ac2013-08-26 14:20:16 -0700118 *
Chet Haasefaebd8f2012-05-18 14:17:57 -0700119 */
Chet Haase6ebe3de2013-06-17 16:50:50 -0700120public abstract class Transition implements Cloneable {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700121
122 private static final String LOG_TAG = "Transition";
123 static final boolean DBG = false;
124
George Mount7b750622014-05-13 18:08:38 -0700125 /**
126 * With {@link #setMatchOrder(int...)}, chooses to match by View instance.
127 */
128 public static final int MATCH_INSTANCE = 0x1;
129 private static final int MATCH_FIRST = MATCH_INSTANCE;
130
131 /**
132 * With {@link #setMatchOrder(int...)}, chooses to match by
George Mount0a2ae002014-06-23 14:57:27 +0000133 * {@link android.view.View#getTransitionName()}. Null names will not be matched.
George Mount7b750622014-05-13 18:08:38 -0700134 */
George Mount0a2ae002014-06-23 14:57:27 +0000135 public static final int MATCH_NAME = 0x2;
George Mount7b750622014-05-13 18:08:38 -0700136
137 /**
138 * With {@link #setMatchOrder(int...)}, chooses to match by
139 * {@link android.view.View#getId()}. Negative IDs will not be matched.
140 */
141 public static final int MATCH_ID = 0x3;
142
143 /**
144 * With {@link #setMatchOrder(int...)}, chooses to match by the {@link android.widget.Adapter}
145 * item id. When {@link android.widget.Adapter#hasStableIds()} returns false, no match
146 * will be made for items.
147 */
148 public static final int MATCH_ITEM_ID = 0x4;
149
150 private static final int MATCH_LAST = MATCH_ITEM_ID;
151
George Mountecd857b2014-06-19 07:51:08 -0700152 private static final String MATCH_INSTANCE_STR = "instance";
153 private static final String MATCH_NAME_STR = "name";
154 /** To be removed before L release */
155 private static final String MATCH_VIEW_NAME_STR = "viewName";
156 private static final String MATCH_ID_STR = "id";
157 private static final String MATCH_ITEM_ID_STR = "itemId";
158
George Mount7b750622014-05-13 18:08:38 -0700159 private static final int[] DEFAULT_MATCH_ORDER = {
George Mount0a2ae002014-06-23 14:57:27 +0000160 MATCH_NAME,
George Mount7b750622014-05-13 18:08:38 -0700161 MATCH_INSTANCE,
162 MATCH_ID,
163 MATCH_ITEM_ID,
164 };
165
George Mountecd857b2014-06-19 07:51:08 -0700166 private static final PathMotion STRAIGHT_PATH_MOTION = new PathMotion() {
167 @Override
168 public Path getPath(float startX, float startY, float endX, float endY) {
169 Path path = new Path();
170 path.moveTo(startX, startY);
171 path.lineTo(endX, endY);
172 return path;
173 }
174 };
175
Chet Haase199acdf2013-07-24 18:40:55 -0700176 private String mName = getClass().getName();
177
Chet Haasefaebd8f2012-05-18 14:17:57 -0700178 long mStartDelay = -1;
179 long mDuration = -1;
180 TimeInterpolator mInterpolator = null;
Chet Haased82c8ac2013-08-26 14:20:16 -0700181 ArrayList<Integer> mTargetIds = new ArrayList<Integer>();
182 ArrayList<View> mTargets = new ArrayList<View>();
George Mount30da61d2014-05-09 13:17:52 -0700183 ArrayList<String> mTargetNames = null;
184 ArrayList<Class> mTargetTypes = null;
Chet Haaseff58f922013-09-11 13:08:18 -0700185 ArrayList<Integer> mTargetIdExcludes = null;
186 ArrayList<View> mTargetExcludes = null;
187 ArrayList<Class> mTargetTypeExcludes = null;
George Mount30da61d2014-05-09 13:17:52 -0700188 ArrayList<String> mTargetNameExcludes = null;
Chet Haaseff58f922013-09-11 13:08:18 -0700189 ArrayList<Integer> mTargetIdChildExcludes = null;
190 ArrayList<View> mTargetChildExcludes = null;
191 ArrayList<Class> mTargetTypeChildExcludes = null;
Chet Haase6ebe3de2013-06-17 16:50:50 -0700192 private TransitionValuesMaps mStartValues = new TransitionValuesMaps();
193 private TransitionValuesMaps mEndValues = new TransitionValuesMaps();
Chet Haased82c8ac2013-08-26 14:20:16 -0700194 TransitionSet mParent = null;
George Mount23d57f32016-03-08 09:32:07 -0800195 int[] mMatchOrder = DEFAULT_MATCH_ORDER;
George Mount4d1ecf52014-07-29 11:15:54 -0700196 ArrayList<TransitionValues> mStartValuesList; // only valid after playTransition starts
197 ArrayList<TransitionValues> mEndValuesList; // only valid after playTransitions starts
Chet Haasefaebd8f2012-05-18 14:17:57 -0700198
Chet Haase199acdf2013-07-24 18:40:55 -0700199 // Per-animator information used for later canceling when future transitions overlap
200 private static ThreadLocal<ArrayMap<Animator, AnimationInfo>> sRunningAnimators =
201 new ThreadLocal<ArrayMap<Animator, AnimationInfo>>();
202
Chet Haased82c8ac2013-08-26 14:20:16 -0700203 // Scene Root is set at createAnimator() time in the cloned Transition
Chet Haase6ebe3de2013-06-17 16:50:50 -0700204 ViewGroup mSceneRoot = null;
205
Chet Haaseb7a7fc92013-09-20 16:33:08 -0700206 // Whether removing views from their parent is possible. This is only for views
207 // in the start scene, which are no longer in the view hierarchy. This property
208 // is determined by whether the previous Scene was created from a layout
209 // resource, and thus the views from the exited scene are going away anyway
210 // and can be removed as necessary to achieve a particular effect, such as
211 // removing them from parents to add them to overlays.
212 boolean mCanRemoveViews = false;
213
Chet Haasee9d32ea2013-06-04 08:46:42 -0700214 // Track all animators in use in case the transition gets canceled and needs to
215 // cancel running animators
216 private ArrayList<Animator> mCurrentAnimators = new ArrayList<Animator>();
217
Chet Haasefaebd8f2012-05-18 14:17:57 -0700218 // Number of per-target instances of this Transition currently running. This count is
Chet Haase199acdf2013-07-24 18:40:55 -0700219 // determined by calls to start() and end()
Chet Haasefaebd8f2012-05-18 14:17:57 -0700220 int mNumInstances = 0;
221
Chet Haase199acdf2013-07-24 18:40:55 -0700222 // Whether this transition is currently paused, due to a call to pause()
223 boolean mPaused = false;
Chet Haasec43524f2013-07-16 14:40:11 -0700224
Chet Haasea56205c2013-09-10 11:30:22 -0700225 // Whether this transition has ended. Used to avoid pause/resume on transitions
226 // that have completed
227 private boolean mEnded = false;
228
Chet Haasec43524f2013-07-16 14:40:11 -0700229 // The set of listeners to be sent transition lifecycle events.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700230 ArrayList<TransitionListener> mListeners = null;
231
Chet Haased82c8ac2013-08-26 14:20:16 -0700232 // The set of animators collected from calls to createAnimator(),
233 // to be run in runAnimators()
Chet Haase199acdf2013-07-24 18:40:55 -0700234 ArrayList<Animator> mAnimators = new ArrayList<Animator>();
Chet Haasec43524f2013-07-16 14:40:11 -0700235
George Mountd6107a32014-03-10 16:51:16 -0700236 // The function for calculating the Animation start delay.
237 TransitionPropagation mPropagation;
238
239 // The rectangular region for Transitions like Explode and TransitionPropagations
240 // like CircularPropagation
241 EpicenterCallback mEpicenterCallback;
242
George Mountd4c3c912014-06-09 12:31:34 -0700243 // For Fragment shared element transitions, linking views explicitly by mismatching
George Mount0a2ae002014-06-23 14:57:27 +0000244 // transitionNames.
George Mountd4c3c912014-06-09 12:31:34 -0700245 ArrayMap<String, String> mNameOverrides;
246
George Mountecd857b2014-06-19 07:51:08 -0700247 // The function used to interpolate along two-dimensional points. Typically used
248 // for adding curves to x/y View motion.
George Mount23d57f32016-03-08 09:32:07 -0800249 PathMotion mPathMotion = STRAIGHT_PATH_MOTION;
George Mountecd857b2014-06-19 07:51:08 -0700250
Chet Haasefaebd8f2012-05-18 14:17:57 -0700251 /**
252 * Constructs a Transition object with no target objects. A transition with
253 * no targets defaults to running on all target objects in the scene hierarchy
Chet Haased82c8ac2013-08-26 14:20:16 -0700254 * (if the transition is not contained in a TransitionSet), or all target
255 * objects passed down from its parent (if it is in a TransitionSet).
Chet Haasefaebd8f2012-05-18 14:17:57 -0700256 */
257 public Transition() {}
258
259 /**
George Mountecd857b2014-06-19 07:51:08 -0700260 * Perform inflation from XML and apply a class-specific base style from a
261 * theme attribute or style resource. This constructor of Transition allows
262 * subclasses to use their own base style when they are inflating.
263 *
264 * @param context The Context the transition is running in, through which it can
265 * access the current theme, resources, etc.
266 * @param attrs The attributes of the XML tag that is inflating the transition.
267 */
268 public Transition(Context context, AttributeSet attrs) {
269
270 TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Transition);
271 long duration = a.getInt(R.styleable.Transition_duration, -1);
272 if (duration >= 0) {
273 setDuration(duration);
274 }
275 long startDelay = a.getInt(R.styleable.Transition_startDelay, -1);
276 if (startDelay > 0) {
277 setStartDelay(startDelay);
278 }
279 final int resID = a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
280 if (resID > 0) {
281 setInterpolator(AnimationUtils.loadInterpolator(context, resID));
282 }
283 String matchOrder = a.getString(R.styleable.Transition_matchOrder);
284 if (matchOrder != null) {
285 setMatchOrder(parseMatchOrder(matchOrder));
286 }
287 a.recycle();
288 }
289
290 private static int[] parseMatchOrder(String matchOrderString) {
291 StringTokenizer st = new StringTokenizer(matchOrderString, ",");
292 int matches[] = new int[st.countTokens()];
293 int index = 0;
294 while (st.hasMoreTokens()) {
295 String token = st.nextToken().trim();
296 if (MATCH_ID_STR.equalsIgnoreCase(token)) {
297 matches[index] = Transition.MATCH_ID;
298 } else if (MATCH_INSTANCE_STR.equalsIgnoreCase(token)) {
299 matches[index] = Transition.MATCH_INSTANCE;
300 } else if (MATCH_NAME_STR.equalsIgnoreCase(token)) {
301 matches[index] = Transition.MATCH_NAME;
302 } else if (MATCH_VIEW_NAME_STR.equalsIgnoreCase(token)) {
303 matches[index] = Transition.MATCH_NAME;
304 } else if (MATCH_ITEM_ID_STR.equalsIgnoreCase(token)) {
305 matches[index] = Transition.MATCH_ITEM_ID;
306 } else if (token.isEmpty()) {
307 int[] smallerMatches = new int[matches.length - 1];
308 System.arraycopy(matches, 0, smallerMatches, 0, index);
309 matches = smallerMatches;
310 index--;
311 } else {
312 throw new InflateException("Unknown match type in matchOrder: '" + token + "'");
313 }
314 index++;
315 }
316 return matches;
317 }
318
319 /**
Chet Haasefaebd8f2012-05-18 14:17:57 -0700320 * Sets the duration of this transition. By default, there is no duration
321 * (indicated by a negative number), which means that the Animator created by
322 * the transition will have its own specified duration. If the duration of a
323 * Transition is set, that duration will override the Animator duration.
324 *
325 * @param duration The length of the animation, in milliseconds.
326 * @return This transition object.
Chet Haased82c8ac2013-08-26 14:20:16 -0700327 * @attr ref android.R.styleable#Transition_duration
Chet Haasefaebd8f2012-05-18 14:17:57 -0700328 */
329 public Transition setDuration(long duration) {
330 mDuration = duration;
331 return this;
332 }
333
Chet Haase199acdf2013-07-24 18:40:55 -0700334 /**
335 * Returns the duration set on this transition. If no duration has been set,
336 * the returned value will be negative, indicating that resulting animators will
337 * retain their own durations.
338 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700339 * @return The duration set on this transition, in milliseconds, if one has been
340 * set, otherwise returns a negative number.
Chet Haase199acdf2013-07-24 18:40:55 -0700341 */
Chet Haasefaebd8f2012-05-18 14:17:57 -0700342 public long getDuration() {
343 return mDuration;
344 }
345
346 /**
347 * Sets the startDelay of this transition. By default, there is no delay
348 * (indicated by a negative number), which means that the Animator created by
349 * the transition will have its own specified startDelay. If the delay of a
350 * Transition is set, that delay will override the Animator delay.
351 *
352 * @param startDelay The length of the delay, in milliseconds.
Chet Haased82c8ac2013-08-26 14:20:16 -0700353 * @return This transition object.
354 * @attr ref android.R.styleable#Transition_startDelay
Chet Haasefaebd8f2012-05-18 14:17:57 -0700355 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700356 public Transition setStartDelay(long startDelay) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700357 mStartDelay = startDelay;
Chet Haased82c8ac2013-08-26 14:20:16 -0700358 return this;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700359 }
360
Chet Haase199acdf2013-07-24 18:40:55 -0700361 /**
362 * Returns the startDelay set on this transition. If no startDelay has been set,
363 * the returned value will be negative, indicating that resulting animators will
364 * retain their own startDelays.
365 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700366 * @return The startDelay set on this transition, in milliseconds, if one has
367 * been set, otherwise returns a negative number.
Chet Haase199acdf2013-07-24 18:40:55 -0700368 */
Chet Haasefaebd8f2012-05-18 14:17:57 -0700369 public long getStartDelay() {
370 return mStartDelay;
371 }
372
373 /**
374 * Sets the interpolator of this transition. By default, the interpolator
375 * is null, which means that the Animator created by the transition
376 * will have its own specified interpolator. If the interpolator of a
377 * Transition is set, that interpolator will override the Animator interpolator.
378 *
379 * @param interpolator The time interpolator used by the transition
Chet Haased82c8ac2013-08-26 14:20:16 -0700380 * @return This transition object.
381 * @attr ref android.R.styleable#Transition_interpolator
Chet Haasefaebd8f2012-05-18 14:17:57 -0700382 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700383 public Transition setInterpolator(TimeInterpolator interpolator) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700384 mInterpolator = interpolator;
Chet Haased82c8ac2013-08-26 14:20:16 -0700385 return this;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700386 }
387
Chet Haase199acdf2013-07-24 18:40:55 -0700388 /**
389 * Returns the interpolator set on this transition. If no interpolator has been set,
390 * the returned value will be null, indicating that resulting animators will
391 * retain their own interpolators.
392 *
393 * @return The interpolator set on this transition, if one has been set, otherwise
394 * returns null.
395 */
Chet Haasefaebd8f2012-05-18 14:17:57 -0700396 public TimeInterpolator getInterpolator() {
397 return mInterpolator;
398 }
399
400 /**
Chet Haase199acdf2013-07-24 18:40:55 -0700401 * Returns the set of property names used stored in the {@link TransitionValues}
Chet Haased82c8ac2013-08-26 14:20:16 -0700402 * object passed into {@link #captureStartValues(TransitionValues)} that
Chet Haase199acdf2013-07-24 18:40:55 -0700403 * this transition cares about for the purposes of canceling overlapping animations.
404 * When any transition is started on a given scene root, all transitions
405 * currently running on that same scene root are checked to see whether the
406 * properties on which they based their animations agree with the end values of
407 * the same properties in the new transition. If the end values are not equal,
408 * then the old animation is canceled since the new transition will start a new
409 * animation to these new values. If the values are equal, the old animation is
410 * allowed to continue and no new animation is started for that transition.
411 *
412 * <p>A transition does not need to override this method. However, not doing so
413 * will mean that the cancellation logic outlined in the previous paragraph
414 * will be skipped for that transition, possibly leading to artifacts as
415 * old transitions and new transitions on the same targets run in parallel,
416 * animating views toward potentially different end values.</p>
417 *
418 * @return An array of property names as described in the class documentation for
419 * {@link TransitionValues}. The default implementation returns <code>null</code>.
420 */
421 public String[] getTransitionProperties() {
422 return null;
423 }
424
425 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700426 * This method creates an animation that will be run for this transition
427 * given the information in the startValues and endValues structures captured
428 * earlier for the start and end scenes. Subclasses of Transition should override
429 * this method. The method should only be called by the transition system; it is
430 * not intended to be called from external classes.
431 *
432 * <p>This method is called by the transition's parent (all the way up to the
Chet Haasefaebd8f2012-05-18 14:17:57 -0700433 * topmost Transition in the hierarchy) with the sceneRoot and start/end
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700434 * values that the transition may need to set up initial target values
435 * and construct an appropriate animation. For example, if an overall
Chet Haased82c8ac2013-08-26 14:20:16 -0700436 * Transition is a {@link TransitionSet} consisting of several
Chet Haasefaebd8f2012-05-18 14:17:57 -0700437 * child transitions in sequence, then some of the child transitions may
438 * want to set initial values on target views prior to the overall
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700439 * Transition commencing, to put them in an appropriate state for the
Chet Haasefaebd8f2012-05-18 14:17:57 -0700440 * delay between that start and the child Transition start time. For
441 * example, a transition that fades an item in may wish to set the starting
442 * alpha value to 0, to avoid it blinking in prior to the transition
443 * actually starting the animation. This is necessary because the scene
444 * change that triggers the Transition will automatically set the end-scene
445 * on all target views, so a Transition that wants to animate from a
Chet Haased82c8ac2013-08-26 14:20:16 -0700446 * different value should set that value prior to returning from this method.</p>
Chet Haasefaebd8f2012-05-18 14:17:57 -0700447 *
448 * <p>Additionally, a Transition can perform logic to determine whether
449 * the transition needs to run on the given target and start/end values.
450 * For example, a transition that resizes objects on the screen may wish
451 * to avoid running for views which are not present in either the start
Chet Haased82c8ac2013-08-26 14:20:16 -0700452 * or end scenes.</p>
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700453 *
454 * <p>If there is an animator created and returned from this method, the
455 * transition mechanism will apply any applicable duration, startDelay,
456 * and interpolator to that animation and start it. A return value of
457 * <code>null</code> indicates that no animation should run. The default
458 * implementation returns null.</p>
Chet Haasefaebd8f2012-05-18 14:17:57 -0700459 *
460 * <p>The method is called for every applicable target object, which is
461 * stored in the {@link TransitionValues#view} field.</p>
462 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700463 *
464 * @param sceneRoot The root of the transition hierarchy.
465 * @param startValues The values for a specific target in the start scene.
466 * @param endValues The values for the target in the end scene.
467 * @return A Animator to be started at the appropriate time in the
468 * overall transition for this scene change. A null value means no animation
469 * should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700470 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700471 public Animator createAnimator(ViewGroup sceneRoot, TransitionValues startValues,
Chet Haasefaebd8f2012-05-18 14:17:57 -0700472 TransitionValues endValues) {
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700473 return null;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700474 }
475
476 /**
George Mount7b750622014-05-13 18:08:38 -0700477 * Sets the order in which Transition matches View start and end values.
478 * <p>
George Mount0a2ae002014-06-23 14:57:27 +0000479 * The default behavior is to match first by {@link android.view.View#getTransitionName()},
George Mount7b750622014-05-13 18:08:38 -0700480 * then by View instance, then by {@link android.view.View#getId()} and finally
481 * by its item ID if it is in a direct child of ListView. The caller can
482 * choose to have only some or all of the values of {@link #MATCH_INSTANCE},
George Mount0a2ae002014-06-23 14:57:27 +0000483 * {@link #MATCH_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}. Only
George Mount7b750622014-05-13 18:08:38 -0700484 * the match algorithms supplied will be used to determine whether Views are the
485 * the same in both the start and end Scene. Views that do not match will be considered
486 * as entering or leaving the Scene.
487 * </p>
488 * @param matches A list of zero or more of {@link #MATCH_INSTANCE},
George Mount0a2ae002014-06-23 14:57:27 +0000489 * {@link #MATCH_NAME}, {@link #MATCH_ITEM_ID}, and {@link #MATCH_ID}.
George Mount7b750622014-05-13 18:08:38 -0700490 * If none are provided, then the default match order will be set.
491 */
492 public void setMatchOrder(int... matches) {
493 if (matches == null || matches.length == 0) {
494 mMatchOrder = DEFAULT_MATCH_ORDER;
495 } else {
496 for (int i = 0; i < matches.length; i++) {
497 int match = matches[i];
498 if (!isValidMatch(match)) {
499 throw new IllegalArgumentException("matches contains invalid value");
500 }
501 if (alreadyContains(matches, i)) {
502 throw new IllegalArgumentException("matches contains a duplicate value");
503 }
504 }
505 mMatchOrder = matches.clone();
506 }
507 }
508
509 private static boolean isValidMatch(int match) {
510 return (match >= MATCH_FIRST && match <= MATCH_LAST);
511 }
512
513 private static boolean alreadyContains(int[] array, int searchIndex) {
514 int value = array[searchIndex];
515 for (int i = 0; i < searchIndex; i++) {
516 if (array[i] == value) {
517 return true;
518 }
519 }
520 return false;
521 }
522
523 /**
George Mount4d1ecf52014-07-29 11:15:54 -0700524 * Match start/end values by View instance. Adds matched values to mStartValuesList
525 * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd.
George Mount30da61d2014-05-09 13:17:52 -0700526 */
George Mount4d1ecf52014-07-29 11:15:54 -0700527 private void matchInstances(ArrayMap<View, TransitionValues> unmatchedStart,
George Mount30da61d2014-05-09 13:17:52 -0700528 ArrayMap<View, TransitionValues> unmatchedEnd) {
529 for (int i = unmatchedStart.size() - 1; i >= 0; i--) {
530 View view = unmatchedStart.keyAt(i);
George Mounte3a4cb52015-06-25 08:59:26 -0700531 if (view != null && isValidTarget(view)) {
532 TransitionValues end = unmatchedEnd.remove(view);
533 if (end != null && end.view != null && isValidTarget(end.view)) {
534 TransitionValues start = unmatchedStart.removeAt(i);
535 mStartValuesList.add(start);
536 mEndValuesList.add(end);
537 }
George Mount30da61d2014-05-09 13:17:52 -0700538 }
539 }
540 }
541
542 /**
George Mount4d1ecf52014-07-29 11:15:54 -0700543 * Match start/end values by Adapter item ID. Adds matched values to mStartValuesList
544 * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd, using
George Mount30da61d2014-05-09 13:17:52 -0700545 * startItemIds and endItemIds as a guide for which Views have unique item IDs.
546 */
George Mount4d1ecf52014-07-29 11:15:54 -0700547 private void matchItemIds(ArrayMap<View, TransitionValues> unmatchedStart,
George Mount30da61d2014-05-09 13:17:52 -0700548 ArrayMap<View, TransitionValues> unmatchedEnd,
549 LongSparseArray<View> startItemIds, LongSparseArray<View> endItemIds) {
550 int numStartIds = startItemIds.size();
551 for (int i = 0; i < numStartIds; i++) {
552 View startView = startItemIds.valueAt(i);
George Mounte3a4cb52015-06-25 08:59:26 -0700553 if (startView != null && isValidTarget(startView)) {
George Mount30da61d2014-05-09 13:17:52 -0700554 View endView = endItemIds.get(startItemIds.keyAt(i));
George Mounte3a4cb52015-06-25 08:59:26 -0700555 if (endView != null && isValidTarget(endView)) {
George Mount30da61d2014-05-09 13:17:52 -0700556 TransitionValues startValues = unmatchedStart.get(startView);
557 TransitionValues endValues = unmatchedEnd.get(endView);
558 if (startValues != null && endValues != null) {
George Mount4d1ecf52014-07-29 11:15:54 -0700559 mStartValuesList.add(startValues);
560 mEndValuesList.add(endValues);
George Mount30da61d2014-05-09 13:17:52 -0700561 unmatchedStart.remove(startView);
562 unmatchedEnd.remove(endView);
563 }
564 }
565 }
566 }
567 }
568
569 /**
George Mount4d1ecf52014-07-29 11:15:54 -0700570 * Match start/end values by Adapter view ID. Adds matched values to mStartValuesList
571 * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd, using
George Mount30da61d2014-05-09 13:17:52 -0700572 * startIds and endIds as a guide for which Views have unique IDs.
573 */
George Mount4d1ecf52014-07-29 11:15:54 -0700574 private void matchIds(ArrayMap<View, TransitionValues> unmatchedStart,
George Mount30da61d2014-05-09 13:17:52 -0700575 ArrayMap<View, TransitionValues> unmatchedEnd,
576 SparseArray<View> startIds, SparseArray<View> endIds) {
577 int numStartIds = startIds.size();
578 for (int i = 0; i < numStartIds; i++) {
579 View startView = startIds.valueAt(i);
580 if (startView != null && isValidTarget(startView)) {
581 View endView = endIds.get(startIds.keyAt(i));
582 if (endView != null && isValidTarget(endView)) {
583 TransitionValues startValues = unmatchedStart.get(startView);
584 TransitionValues endValues = unmatchedEnd.get(endView);
585 if (startValues != null && endValues != null) {
George Mount4d1ecf52014-07-29 11:15:54 -0700586 mStartValuesList.add(startValues);
587 mEndValuesList.add(endValues);
George Mount30da61d2014-05-09 13:17:52 -0700588 unmatchedStart.remove(startView);
589 unmatchedEnd.remove(endView);
590 }
591 }
592 }
593 }
594 }
595
596 /**
George Mount4d1ecf52014-07-29 11:15:54 -0700597 * Match start/end values by Adapter transitionName. Adds matched values to mStartValuesList
598 * and mEndValuesList and removes them from unmatchedStart and unmatchedEnd, using
George Mount0a2ae002014-06-23 14:57:27 +0000599 * startNames and endNames as a guide for which Views have unique transitionNames.
George Mount30da61d2014-05-09 13:17:52 -0700600 */
George Mount4d1ecf52014-07-29 11:15:54 -0700601 private void matchNames(ArrayMap<View, TransitionValues> unmatchedStart,
George Mount30da61d2014-05-09 13:17:52 -0700602 ArrayMap<View, TransitionValues> unmatchedEnd,
603 ArrayMap<String, View> startNames, ArrayMap<String, View> endNames) {
604 int numStartNames = startNames.size();
605 for (int i = 0; i < numStartNames; i++) {
606 View startView = startNames.valueAt(i);
607 if (startView != null && isValidTarget(startView)) {
608 View endView = endNames.get(startNames.keyAt(i));
609 if (endView != null && isValidTarget(endView)) {
610 TransitionValues startValues = unmatchedStart.get(startView);
611 TransitionValues endValues = unmatchedEnd.get(endView);
612 if (startValues != null && endValues != null) {
George Mount4d1ecf52014-07-29 11:15:54 -0700613 mStartValuesList.add(startValues);
614 mEndValuesList.add(endValues);
George Mount30da61d2014-05-09 13:17:52 -0700615 unmatchedStart.remove(startView);
616 unmatchedEnd.remove(endView);
617 }
618 }
619 }
620 }
621 }
622
623 /**
George Mount4d1ecf52014-07-29 11:15:54 -0700624 * Adds all values from unmatchedStart and unmatchedEnd to mStartValuesList and mEndValuesList,
George Mount30da61d2014-05-09 13:17:52 -0700625 * assuming that there is no match between values in the list.
626 */
George Mount4d1ecf52014-07-29 11:15:54 -0700627 private void addUnmatched(ArrayMap<View, TransitionValues> unmatchedStart,
George Mount30da61d2014-05-09 13:17:52 -0700628 ArrayMap<View, TransitionValues> unmatchedEnd) {
629 // Views that only exist in the start Scene
630 for (int i = 0; i < unmatchedStart.size(); i++) {
George Mounte3a4cb52015-06-25 08:59:26 -0700631 final TransitionValues start = unmatchedStart.valueAt(i);
632 if (isValidTarget(start.view)) {
633 mStartValuesList.add(start);
634 mEndValuesList.add(null);
635 }
George Mount30da61d2014-05-09 13:17:52 -0700636 }
637
638 // Views that only exist in the end Scene
639 for (int i = 0; i < unmatchedEnd.size(); i++) {
George Mounte3a4cb52015-06-25 08:59:26 -0700640 final TransitionValues end = unmatchedEnd.valueAt(i);
641 if (isValidTarget(end.view)) {
642 mEndValuesList.add(end);
643 mStartValuesList.add(null);
644 }
George Mount30da61d2014-05-09 13:17:52 -0700645 }
646 }
647
George Mount7b750622014-05-13 18:08:38 -0700648 private void matchStartAndEnd(TransitionValuesMaps startValues,
George Mount4d1ecf52014-07-29 11:15:54 -0700649 TransitionValuesMaps endValues) {
George Mount7b750622014-05-13 18:08:38 -0700650 ArrayMap<View, TransitionValues> unmatchedStart =
651 new ArrayMap<View, TransitionValues>(startValues.viewValues);
652 ArrayMap<View, TransitionValues> unmatchedEnd =
653 new ArrayMap<View, TransitionValues>(endValues.viewValues);
654
655 for (int i = 0; i < mMatchOrder.length; i++) {
656 switch (mMatchOrder[i]) {
657 case MATCH_INSTANCE:
George Mount4d1ecf52014-07-29 11:15:54 -0700658 matchInstances(unmatchedStart, unmatchedEnd);
George Mount7b750622014-05-13 18:08:38 -0700659 break;
George Mount0a2ae002014-06-23 14:57:27 +0000660 case MATCH_NAME:
George Mount4d1ecf52014-07-29 11:15:54 -0700661 matchNames(unmatchedStart, unmatchedEnd,
George Mount7b750622014-05-13 18:08:38 -0700662 startValues.nameValues, endValues.nameValues);
663 break;
664 case MATCH_ID:
George Mount4d1ecf52014-07-29 11:15:54 -0700665 matchIds(unmatchedStart, unmatchedEnd,
George Mount7b750622014-05-13 18:08:38 -0700666 startValues.idValues, endValues.idValues);
667 break;
668 case MATCH_ITEM_ID:
George Mount4d1ecf52014-07-29 11:15:54 -0700669 matchItemIds(unmatchedStart, unmatchedEnd,
George Mount7b750622014-05-13 18:08:38 -0700670 startValues.itemIdValues, endValues.itemIdValues);
671 break;
672 }
673 }
George Mount4d1ecf52014-07-29 11:15:54 -0700674 addUnmatched(unmatchedStart, unmatchedEnd);
George Mount7b750622014-05-13 18:08:38 -0700675 }
676
George Mount30da61d2014-05-09 13:17:52 -0700677 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700678 * This method, essentially a wrapper around all calls to createAnimator for all
679 * possible target views, is called with the entire set of start/end
Chet Haasefaebd8f2012-05-18 14:17:57 -0700680 * values. The implementation in Transition iterates through these lists
Chet Haased82c8ac2013-08-26 14:20:16 -0700681 * and calls {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}
Chet Haasefaebd8f2012-05-18 14:17:57 -0700682 * with each set of start/end values on this transition. The
Chet Haased82c8ac2013-08-26 14:20:16 -0700683 * TransitionSet subclass overrides this method and delegates it to
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700684 * each of its children in succession.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700685 *
686 * @hide
687 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700688 protected void createAnimators(ViewGroup sceneRoot, TransitionValuesMaps startValues,
George Mount4d1ecf52014-07-29 11:15:54 -0700689 TransitionValuesMaps endValues, ArrayList<TransitionValues> startValuesList,
690 ArrayList<TransitionValues> endValuesList) {
Chet Haasec43524f2013-07-16 14:40:11 -0700691 if (DBG) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700692 Log.d(LOG_TAG, "createAnimators() for " + this);
Chet Haasec43524f2013-07-16 14:40:11 -0700693 }
Chet Haase199acdf2013-07-24 18:40:55 -0700694 ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
George Mountd6107a32014-03-10 16:51:16 -0700695 long minStartDelay = Long.MAX_VALUE;
696 int minAnimator = mAnimators.size();
697 SparseLongArray startDelays = new SparseLongArray();
George Mount4d1ecf52014-07-29 11:15:54 -0700698 int startValuesListCount = startValuesList.size();
699 for (int i = 0; i < startValuesListCount; ++i) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700700 TransitionValues start = startValuesList.get(i);
701 TransitionValues end = endValuesList.get(i);
George Mount5030c7f2014-09-10 10:58:08 -0700702 if (start != null && !start.targetedTransitions.contains(this)) {
703 start = null;
704 }
705 if (end != null && !end.targetedTransitions.contains(this)) {
706 end = null;
707 }
708 if (start == null && end == null) {
709 continue;
710 }
711 // Only bother trying to animate with values that differ between start/end
George Mountaef1ae32015-06-04 12:51:55 -0700712 boolean isChanged = start == null || end == null || isTransitionRequired(start, end);
George Mount5030c7f2014-09-10 10:58:08 -0700713 if (isChanged) {
George Mount208dcade2014-06-05 16:47:47 -0700714 if (DBG) {
715 View view = (end != null) ? end.view : start.view;
716 Log.d(LOG_TAG, " differing start/end values for view " + view);
717 if (start == null || end == null) {
718 Log.d(LOG_TAG, " " + ((start == null) ?
719 "start null, end non-null" : "start non-null, end null"));
720 } else {
721 for (String key : start.values.keySet()) {
722 Object startValue = start.values.get(key);
723 Object endValue = end.values.get(key);
724 if (startValue != endValue && !startValue.equals(endValue)) {
725 Log.d(LOG_TAG, " " + key + ": start(" + startValue +
726 "), end(" + endValue + ")");
Chet Haasec43524f2013-07-16 14:40:11 -0700727 }
728 }
729 }
George Mount208dcade2014-06-05 16:47:47 -0700730 }
731 // TODO: what to do about targetIds and itemIds?
732 Animator animator = createAnimator(sceneRoot, start, end);
733 if (animator != null) {
734 // Save animation info for future cancellation purposes
735 View view = null;
736 TransitionValues infoValues = null;
737 if (end != null) {
738 view = end.view;
739 String[] properties = getTransitionProperties();
740 if (view != null && properties != null && properties.length > 0) {
741 infoValues = new TransitionValues();
742 infoValues.view = view;
743 TransitionValues newValues = endValues.viewValues.get(view);
744 if (newValues != null) {
745 for (int j = 0; j < properties.length; ++j) {
746 infoValues.values.put(properties[j],
747 newValues.values.get(properties[j]));
748 }
749 }
750 int numExistingAnims = runningAnimators.size();
751 for (int j = 0; j < numExistingAnims; ++j) {
752 Animator anim = runningAnimators.keyAt(j);
753 AnimationInfo info = runningAnimators.get(anim);
754 if (info.values != null && info.view == view &&
755 ((info.name == null && getName() == null) ||
756 info.name.equals(getName()))) {
757 if (info.values.equals(infoValues)) {
758 // Favor the old animator
759 animator = null;
760 break;
761 }
762 }
763 }
764 }
765 } else {
766 view = (start != null) ? start.view : null;
767 }
Chet Haasec43524f2013-07-16 14:40:11 -0700768 if (animator != null) {
George Mount208dcade2014-06-05 16:47:47 -0700769 if (mPropagation != null) {
770 long delay = mPropagation
771 .getStartDelay(sceneRoot, this, start, end);
772 startDelays.put(mAnimators.size(), delay);
773 minStartDelay = Math.min(delay, minStartDelay);
Chet Haase199acdf2013-07-24 18:40:55 -0700774 }
George Mount4c20ea22014-06-17 10:14:39 -0700775 AnimationInfo info = new AnimationInfo(view, getName(), this,
George Mount208dcade2014-06-05 16:47:47 -0700776 sceneRoot.getWindowId(), infoValues);
777 runningAnimators.put(animator, info);
778 mAnimators.add(animator);
Chet Haasec43524f2013-07-16 14:40:11 -0700779 }
Chet Haasec43524f2013-07-16 14:40:11 -0700780 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700781 }
782 }
George Mount23d57f32016-03-08 09:32:07 -0800783 if (startDelays.size() != 0) {
George Mountd6107a32014-03-10 16:51:16 -0700784 for (int i = 0; i < startDelays.size(); i++) {
785 int index = startDelays.keyAt(i);
786 Animator animator = mAnimators.get(index);
787 long delay = startDelays.valueAt(i) - minStartDelay + animator.getStartDelay();
788 animator.setStartDelay(delay);
789 }
790 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700791 }
792
793 /**
794 * Internal utility method for checking whether a given view/id
795 * is valid for this transition, where "valid" means that either
796 * the Transition has no target/targetId list (the default, in which
797 * cause the transition should act on all views in the hiearchy), or
798 * the given view is in the target list or the view id is in the
799 * targetId list. If the target parameter is null, then the target list
800 * is not checked (this is in the case of ListView items, where the
801 * views are ignored and only the ids are used).
802 */
George Mount30da61d2014-05-09 13:17:52 -0700803 boolean isValidTarget(View target) {
George Mountf586c922015-05-04 15:01:55 -0700804 if (target == null) {
805 return false;
806 }
George Mount30da61d2014-05-09 13:17:52 -0700807 int targetId = target.getId();
Chet Haaseff58f922013-09-11 13:08:18 -0700808 if (mTargetIdExcludes != null && mTargetIdExcludes.contains(targetId)) {
809 return false;
810 }
811 if (mTargetExcludes != null && mTargetExcludes.contains(target)) {
812 return false;
813 }
814 if (mTargetTypeExcludes != null && target != null) {
815 int numTypes = mTargetTypeExcludes.size();
816 for (int i = 0; i < numTypes; ++i) {
817 Class type = mTargetTypeExcludes.get(i);
818 if (type.isInstance(target)) {
819 return false;
820 }
821 }
822 }
George Mount0a2ae002014-06-23 14:57:27 +0000823 if (mTargetNameExcludes != null && target != null && target.getTransitionName() != null) {
824 if (mTargetNameExcludes.contains(target.getTransitionName())) {
George Mount30da61d2014-05-09 13:17:52 -0700825 return false;
826 }
827 }
828 if (mTargetIds.size() == 0 && mTargets.size() == 0 &&
George Mountcc824242014-09-18 11:21:09 -0700829 (mTargetTypes == null || mTargetTypes.isEmpty()) &&
830 (mTargetNames == null || mTargetNames.isEmpty())) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700831 return true;
832 }
George Mount30da61d2014-05-09 13:17:52 -0700833 if (mTargetIds.contains(targetId) || mTargets.contains(target)) {
834 return true;
835 }
George Mount0a2ae002014-06-23 14:57:27 +0000836 if (mTargetNames != null && mTargetNames.contains(target.getTransitionName())) {
George Mounta98fb7a2014-04-24 08:57:03 -0700837 return true;
Chet Haasefaebd8f2012-05-18 14:17:57 -0700838 }
George Mounta98fb7a2014-04-24 08:57:03 -0700839 if (mTargetTypes != null) {
840 for (int i = 0; i < mTargetTypes.size(); ++i) {
841 if (mTargetTypes.get(i).isInstance(target)) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700842 return true;
843 }
844 }
845 }
846 return false;
847 }
848
George Mounte1803372014-02-26 19:00:52 +0000849 private static ArrayMap<Animator, AnimationInfo> getRunningAnimators() {
Chet Haase199acdf2013-07-24 18:40:55 -0700850 ArrayMap<Animator, AnimationInfo> runningAnimators = sRunningAnimators.get();
851 if (runningAnimators == null) {
852 runningAnimators = new ArrayMap<Animator, AnimationInfo>();
853 sRunningAnimators.set(runningAnimators);
854 }
855 return runningAnimators;
856 }
857
Chet Haasefaebd8f2012-05-18 14:17:57 -0700858 /**
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700859 * This is called internally once all animations have been set up by the
George Mountd6107a32014-03-10 16:51:16 -0700860 * transition hierarchy.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700861 *
862 * @hide
863 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700864 protected void runAnimators() {
Chet Haase199acdf2013-07-24 18:40:55 -0700865 if (DBG) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700866 Log.d(LOG_TAG, "runAnimators() on " + this);
Chet Haasec43524f2013-07-16 14:40:11 -0700867 }
Chet Haase199acdf2013-07-24 18:40:55 -0700868 start();
869 ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
Chet Haased82c8ac2013-08-26 14:20:16 -0700870 // Now start every Animator that was previously created for this transition
Chet Haase199acdf2013-07-24 18:40:55 -0700871 for (Animator anim : mAnimators) {
Chet Haasec43524f2013-07-16 14:40:11 -0700872 if (DBG) {
873 Log.d(LOG_TAG, " anim: " + anim);
874 }
Chet Haase199acdf2013-07-24 18:40:55 -0700875 if (runningAnimators.containsKey(anim)) {
876 start();
877 runAnimator(anim, runningAnimators);
878 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700879 }
Chet Haase199acdf2013-07-24 18:40:55 -0700880 mAnimators.clear();
881 end();
Chet Haasefaebd8f2012-05-18 14:17:57 -0700882 }
883
Chet Haase199acdf2013-07-24 18:40:55 -0700884 private void runAnimator(Animator animator,
885 final ArrayMap<Animator, AnimationInfo> runningAnimators) {
Chet Haasee9d32ea2013-06-04 08:46:42 -0700886 if (animator != null) {
887 // TODO: could be a single listener instance for all of them since it uses the param
888 animator.addListener(new AnimatorListenerAdapter() {
889 @Override
890 public void onAnimationStart(Animator animation) {
891 mCurrentAnimators.add(animation);
892 }
893 @Override
894 public void onAnimationEnd(Animator animation) {
Chet Haase199acdf2013-07-24 18:40:55 -0700895 runningAnimators.remove(animation);
Chet Haasee9d32ea2013-06-04 08:46:42 -0700896 mCurrentAnimators.remove(animation);
897 }
898 });
899 animate(animator);
900 }
901 }
902
Chet Haasefaebd8f2012-05-18 14:17:57 -0700903 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700904 * Captures the values in the start scene for the properties that this
905 * transition monitors. These values are then passed as the startValues
906 * structure in a later call to
907 * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
908 * The main concern for an implementation is what the
Chet Haasefaebd8f2012-05-18 14:17:57 -0700909 * properties are that the transition cares about and what the values are
910 * for all of those properties. The start and end values will be compared
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700911 * later during the
Chet Haased82c8ac2013-08-26 14:20:16 -0700912 * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700913 * method to determine what, if any, animations, should be run.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700914 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700915 * <p>Subclasses must implement this method. The method should only be called by the
916 * transition system; it is not intended to be called from external classes.</p>
917 *
Chet Haase2ea7f8b2013-06-21 15:00:05 -0700918 * @param transitionValues The holder for any values that the Transition
919 * wishes to store. Values are stored in the <code>values</code> field
920 * of this TransitionValues object and are keyed from
921 * a String value. For example, to store a view's rotation value,
922 * a transition might call
923 * <code>transitionValues.values.put("appname:transitionname:rotation",
924 * view.getRotation())</code>. The target view will already be stored in
925 * the transitionValues structure when this method is called.
Chet Haased82c8ac2013-08-26 14:20:16 -0700926 *
927 * @see #captureEndValues(TransitionValues)
928 * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
Chet Haasefaebd8f2012-05-18 14:17:57 -0700929 */
Chet Haased82c8ac2013-08-26 14:20:16 -0700930 public abstract void captureStartValues(TransitionValues transitionValues);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700931
932 /**
Chet Haased82c8ac2013-08-26 14:20:16 -0700933 * Captures the values in the end scene for the properties that this
934 * transition monitors. These values are then passed as the endValues
935 * structure in a later call to
936 * {@link #createAnimator(ViewGroup, TransitionValues, TransitionValues)}.
937 * The main concern for an implementation is what the
938 * properties are that the transition cares about and what the values are
939 * for all of those properties. The start and end values will be compared
940 * later during the
941 * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}
942 * method to determine what, if any, animations, should be run.
943 *
944 * <p>Subclasses must implement this method. The method should only be called by the
945 * transition system; it is not intended to be called from external classes.</p>
946 *
947 * @param transitionValues The holder for any values that the Transition
948 * wishes to store. Values are stored in the <code>values</code> field
949 * of this TransitionValues object and are keyed from
950 * a String value. For example, to store a view's rotation value,
951 * a transition might call
952 * <code>transitionValues.values.put("appname:transitionname:rotation",
953 * view.getRotation())</code>. The target view will already be stored in
954 * the transitionValues structure when this method is called.
955 *
956 * @see #captureStartValues(TransitionValues)
957 * @see #createAnimator(ViewGroup, TransitionValues, TransitionValues)
958 */
959 public abstract void captureEndValues(TransitionValues transitionValues);
960
961 /**
962 * Adds the id of a target view that this Transition is interested in
Chet Haasefaebd8f2012-05-18 14:17:57 -0700963 * animating. By default, there are no targetIds, and a Transition will
964 * listen for changes on every view in the hierarchy below the sceneRoot
Chet Haased82c8ac2013-08-26 14:20:16 -0700965 * of the Scene being transitioned into. Setting targetIds constrains
Chet Haasefaebd8f2012-05-18 14:17:57 -0700966 * the Transition to only listen for, and act on, views with these IDs.
967 * Views with different IDs, or no IDs whatsoever, will be ignored.
968 *
Chet Haased82c8ac2013-08-26 14:20:16 -0700969 * <p>Note that using ids to specify targets implies that ids should be unique
George Mountb592d842014-10-31 17:19:18 -0700970 * within the view hierarchy underneath the scene root.</p>
Chet Haased82c8ac2013-08-26 14:20:16 -0700971 *
Chet Haasefaebd8f2012-05-18 14:17:57 -0700972 * @see View#getId()
Chet Haased82c8ac2013-08-26 14:20:16 -0700973 * @param targetId The id of a target view, must be a positive number.
974 * @return The Transition to which the targetId is added.
Chet Haasefaebd8f2012-05-18 14:17:57 -0700975 * Returning the same object makes it easier to chain calls during
976 * construction, such as
Chet Haaseff58f922013-09-11 13:08:18 -0700977 * <code>transitionSet.addTransitions(new Fade()).addTarget(someId);</code>
Chet Haasefaebd8f2012-05-18 14:17:57 -0700978 */
Chet Haaseff58f922013-09-11 13:08:18 -0700979 public Transition addTarget(int targetId) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700980 if (targetId > 0) {
981 mTargetIds.add(targetId);
982 }
983 return this;
984 }
985
986 /**
George Mount0a2ae002014-06-23 14:57:27 +0000987 * Adds the transitionName of a target view that this Transition is interested in
George Mount30da61d2014-05-09 13:17:52 -0700988 * animating. By default, there are no targetNames, and a Transition will
989 * listen for changes on every view in the hierarchy below the sceneRoot
990 * of the Scene being transitioned into. Setting targetNames constrains
George Mount0a2ae002014-06-23 14:57:27 +0000991 * the Transition to only listen for, and act on, views with these transitionNames.
992 * Views with different transitionNames, or no transitionName whatsoever, will be ignored.
George Mount30da61d2014-05-09 13:17:52 -0700993 *
George Mount0a2ae002014-06-23 14:57:27 +0000994 * <p>Note that transitionNames should be unique within the view hierarchy.</p>
George Mount30da61d2014-05-09 13:17:52 -0700995 *
George Mount0a2ae002014-06-23 14:57:27 +0000996 * @see android.view.View#getTransitionName()
997 * @param targetName The transitionName of a target view, must be non-null.
998 * @return The Transition to which the target transitionName is added.
George Mount30da61d2014-05-09 13:17:52 -0700999 * Returning the same object makes it easier to chain calls during
1000 * construction, such as
1001 * <code>transitionSet.addTransitions(new Fade()).addTarget(someName);</code>
1002 */
1003 public Transition addTarget(String targetName) {
1004 if (targetName != null) {
George Mount58bbdbd2014-06-30 17:31:44 -07001005 if (mTargetNames == null) {
George Mount30da61d2014-05-09 13:17:52 -07001006 mTargetNames = new ArrayList<String>();
1007 }
1008 mTargetNames.add(targetName);
1009 }
1010 return this;
1011 }
1012
1013 /**
George Mounta98fb7a2014-04-24 08:57:03 -07001014 * Adds the Class of a target view that this Transition is interested in
1015 * animating. By default, there are no targetTypes, and a Transition will
1016 * listen for changes on every view in the hierarchy below the sceneRoot
1017 * of the Scene being transitioned into. Setting targetTypes constrains
1018 * the Transition to only listen for, and act on, views with these classes.
1019 * Views with different classes will be ignored.
1020 *
1021 * <p>Note that any View that can be cast to targetType will be included, so
1022 * if targetType is <code>View.class</code>, all Views will be included.</p>
1023 *
1024 * @see #addTarget(int)
1025 * @see #addTarget(android.view.View)
1026 * @see #excludeTarget(Class, boolean)
1027 * @see #excludeChildren(Class, boolean)
1028 *
1029 * @param targetType The type to include when running this transition.
1030 * @return The Transition to which the target class was added.
1031 * Returning the same object makes it easier to chain calls during
1032 * construction, such as
1033 * <code>transitionSet.addTransitions(new Fade()).addTarget(ImageView.class);</code>
1034 */
1035 public Transition addTarget(Class targetType) {
George Mount30da61d2014-05-09 13:17:52 -07001036 if (targetType != null) {
1037 if (mTargetTypes == null) {
1038 mTargetTypes = new ArrayList<Class>();
1039 }
1040 mTargetTypes.add(targetType);
George Mounta98fb7a2014-04-24 08:57:03 -07001041 }
George Mounta98fb7a2014-04-24 08:57:03 -07001042 return this;
1043 }
1044
1045 /**
Chet Haased82c8ac2013-08-26 14:20:16 -07001046 * Removes the given targetId from the list of ids that this Transition
1047 * is interested in animating.
1048 *
1049 * @param targetId The id of a target view, must be a positive number.
1050 * @return The Transition from which the targetId is removed.
1051 * Returning the same object makes it easier to chain calls during
1052 * construction, such as
1053 * <code>transitionSet.addTransitions(new Fade()).removeTargetId(someId);</code>
1054 */
Chet Haaseff58f922013-09-11 13:08:18 -07001055 public Transition removeTarget(int targetId) {
Chet Haased82c8ac2013-08-26 14:20:16 -07001056 if (targetId > 0) {
George Mount5697c322015-06-23 15:42:37 -07001057 mTargetIds.remove((Integer)targetId);
Chet Haased82c8ac2013-08-26 14:20:16 -07001058 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07001059 return this;
1060 }
1061
1062 /**
George Mount0a2ae002014-06-23 14:57:27 +00001063 * Removes the given targetName from the list of transitionNames that this Transition
George Mount30da61d2014-05-09 13:17:52 -07001064 * is interested in animating.
1065 *
George Mount0a2ae002014-06-23 14:57:27 +00001066 * @param targetName The transitionName of a target view, must not be null.
George Mount30da61d2014-05-09 13:17:52 -07001067 * @return The Transition from which the targetName is removed.
1068 * Returning the same object makes it easier to chain calls during
1069 * construction, such as
1070 * <code>transitionSet.addTransitions(new Fade()).removeTargetName(someName);</code>
1071 */
1072 public Transition removeTarget(String targetName) {
1073 if (targetName != null && mTargetNames != null) {
1074 mTargetNames.remove(targetName);
1075 }
1076 return this;
1077 }
1078
1079 /**
Chet Haaseff58f922013-09-11 13:08:18 -07001080 * Whether to add the given id to the list of target ids to exclude from this
1081 * transition. The <code>exclude</code> parameter specifies whether the target
1082 * should be added to or removed from the excluded list.
1083 *
1084 * <p>Excluding targets is a general mechanism for allowing transitions to run on
1085 * a view hierarchy while skipping target views that should not be part of
1086 * the transition. For example, you may want to avoid animating children
1087 * of a specific ListView or Spinner. Views can be excluded either by their
1088 * id, or by their instance reference, or by the Class of that view
1089 * (eg, {@link Spinner}).</p>
1090 *
1091 * @see #excludeChildren(int, boolean)
1092 * @see #excludeTarget(View, boolean)
1093 * @see #excludeTarget(Class, boolean)
1094 *
1095 * @param targetId The id of a target to ignore when running this transition.
1096 * @param exclude Whether to add the target to or remove the target from the
1097 * current list of excluded targets.
1098 * @return This transition object.
1099 */
1100 public Transition excludeTarget(int targetId, boolean exclude) {
George Mount30da61d2014-05-09 13:17:52 -07001101 if (targetId >= 0) {
1102 mTargetIdExcludes = excludeObject(mTargetIdExcludes, targetId, exclude);
1103 }
1104 return this;
1105 }
1106
1107 /**
George Mount0a2ae002014-06-23 14:57:27 +00001108 * Whether to add the given transitionName to the list of target transitionNames to exclude
1109 * from this transition. The <code>exclude</code> parameter specifies whether the target
George Mount30da61d2014-05-09 13:17:52 -07001110 * should be added to or removed from the excluded list.
1111 *
1112 * <p>Excluding targets is a general mechanism for allowing transitions to run on
1113 * a view hierarchy while skipping target views that should not be part of
1114 * the transition. For example, you may want to avoid animating children
1115 * of a specific ListView or Spinner. Views can be excluded by their
George Mount0a2ae002014-06-23 14:57:27 +00001116 * id, their instance reference, their transitionName, or by the Class of that view
George Mount30da61d2014-05-09 13:17:52 -07001117 * (eg, {@link Spinner}).</p>
1118 *
1119 * @see #excludeTarget(View, boolean)
1120 * @see #excludeTarget(int, boolean)
1121 * @see #excludeTarget(Class, boolean)
1122 *
George Mount0a2ae002014-06-23 14:57:27 +00001123 * @param targetName The name of a target to ignore when running this transition.
George Mount30da61d2014-05-09 13:17:52 -07001124 * @param exclude Whether to add the target to or remove the target from the
1125 * current list of excluded targets.
1126 * @return This transition object.
1127 */
George Mount0a2ae002014-06-23 14:57:27 +00001128 public Transition excludeTarget(String targetName, boolean exclude) {
1129 mTargetNameExcludes = excludeObject(mTargetNameExcludes, targetName, exclude);
Chet Haaseff58f922013-09-11 13:08:18 -07001130 return this;
1131 }
1132
1133 /**
1134 * Whether to add the children of the given id to the list of targets to exclude
1135 * from this transition. The <code>exclude</code> parameter specifies whether
1136 * the children of the target should be added to or removed from the excluded list.
1137 * Excluding children in this way provides a simple mechanism for excluding all
1138 * children of specific targets, rather than individually excluding each
1139 * child individually.
1140 *
1141 * <p>Excluding targets is a general mechanism for allowing transitions to run on
1142 * a view hierarchy while skipping target views that should not be part of
1143 * the transition. For example, you may want to avoid animating children
1144 * of a specific ListView or Spinner. Views can be excluded either by their
1145 * id, or by their instance reference, or by the Class of that view
1146 * (eg, {@link Spinner}).</p>
1147 *
1148 * @see #excludeTarget(int, boolean)
1149 * @see #excludeChildren(View, boolean)
1150 * @see #excludeChildren(Class, boolean)
1151 *
1152 * @param targetId The id of a target whose children should be ignored when running
1153 * this transition.
1154 * @param exclude Whether to add the target to or remove the target from the
1155 * current list of excluded-child targets.
1156 * @return This transition object.
1157 */
1158 public Transition excludeChildren(int targetId, boolean exclude) {
George Mount30da61d2014-05-09 13:17:52 -07001159 if (targetId >= 0) {
1160 mTargetIdChildExcludes = excludeObject(mTargetIdChildExcludes, targetId, exclude);
Chet Haaseff58f922013-09-11 13:08:18 -07001161 }
George Mount30da61d2014-05-09 13:17:52 -07001162 return this;
Chet Haaseff58f922013-09-11 13:08:18 -07001163 }
1164
1165 /**
1166 * Whether to add the given target to the list of targets to exclude from this
1167 * transition. The <code>exclude</code> parameter specifies whether the target
1168 * should be added to or removed from the excluded list.
1169 *
1170 * <p>Excluding targets is a general mechanism for allowing transitions to run on
1171 * a view hierarchy while skipping target views that should not be part of
1172 * the transition. For example, you may want to avoid animating children
1173 * of a specific ListView or Spinner. Views can be excluded either by their
1174 * id, or by their instance reference, or by the Class of that view
1175 * (eg, {@link Spinner}).</p>
1176 *
1177 * @see #excludeChildren(View, boolean)
1178 * @see #excludeTarget(int, boolean)
1179 * @see #excludeTarget(Class, boolean)
1180 *
1181 * @param target The target to ignore when running this transition.
1182 * @param exclude Whether to add the target to or remove the target from the
1183 * current list of excluded targets.
1184 * @return This transition object.
1185 */
1186 public Transition excludeTarget(View target, boolean exclude) {
George Mount30da61d2014-05-09 13:17:52 -07001187 mTargetExcludes = excludeObject(mTargetExcludes, target, exclude);
Chet Haaseff58f922013-09-11 13:08:18 -07001188 return this;
1189 }
1190
1191 /**
1192 * Whether to add the children of given target to the list of target children
1193 * to exclude from this transition. The <code>exclude</code> parameter specifies
1194 * whether the target should be added to or removed from the excluded list.
1195 *
1196 * <p>Excluding targets is a general mechanism for allowing transitions to run on
1197 * a view hierarchy while skipping target views that should not be part of
1198 * the transition. For example, you may want to avoid animating children
1199 * of a specific ListView or Spinner. Views can be excluded either by their
1200 * id, or by their instance reference, or by the Class of that view
1201 * (eg, {@link Spinner}).</p>
1202 *
1203 * @see #excludeTarget(View, boolean)
1204 * @see #excludeChildren(int, boolean)
1205 * @see #excludeChildren(Class, boolean)
1206 *
1207 * @param target The target to ignore when running this transition.
1208 * @param exclude Whether to add the target to or remove the target from the
1209 * current list of excluded targets.
1210 * @return This transition object.
1211 */
1212 public Transition excludeChildren(View target, boolean exclude) {
George Mount30da61d2014-05-09 13:17:52 -07001213 mTargetChildExcludes = excludeObject(mTargetChildExcludes, target, exclude);
Chet Haaseff58f922013-09-11 13:08:18 -07001214 return this;
1215 }
1216
1217 /**
1218 * Utility method to manage the boilerplate code that is the same whether we
1219 * are excluding targets or their children.
1220 */
George Mount30da61d2014-05-09 13:17:52 -07001221 private static <T> ArrayList<T> excludeObject(ArrayList<T> list, T target, boolean exclude) {
Chet Haaseff58f922013-09-11 13:08:18 -07001222 if (target != null) {
1223 if (exclude) {
1224 list = ArrayListManager.add(list, target);
1225 } else {
1226 list = ArrayListManager.remove(list, target);
1227 }
1228 }
1229 return list;
1230 }
1231
1232 /**
1233 * Whether to add the given type to the list of types to exclude from this
1234 * transition. The <code>exclude</code> parameter specifies whether the target
1235 * type should be added to or removed from the excluded list.
1236 *
1237 * <p>Excluding targets is a general mechanism for allowing transitions to run on
1238 * a view hierarchy while skipping target views that should not be part of
1239 * the transition. For example, you may want to avoid animating children
1240 * of a specific ListView or Spinner. Views can be excluded either by their
1241 * id, or by their instance reference, or by the Class of that view
1242 * (eg, {@link Spinner}).</p>
1243 *
1244 * @see #excludeChildren(Class, boolean)
1245 * @see #excludeTarget(int, boolean)
1246 * @see #excludeTarget(View, boolean)
1247 *
1248 * @param type The type to ignore when running this transition.
1249 * @param exclude Whether to add the target type to or remove it from the
1250 * current list of excluded target types.
1251 * @return This transition object.
1252 */
1253 public Transition excludeTarget(Class type, boolean exclude) {
George Mount30da61d2014-05-09 13:17:52 -07001254 mTargetTypeExcludes = excludeObject(mTargetTypeExcludes, type, exclude);
Chet Haaseff58f922013-09-11 13:08:18 -07001255 return this;
1256 }
1257
1258 /**
1259 * Whether to add the given type to the list of types whose children should
1260 * be excluded from this transition. The <code>exclude</code> parameter
1261 * specifies whether the target type should be added to or removed from
1262 * the excluded list.
1263 *
1264 * <p>Excluding targets is a general mechanism for allowing transitions to run on
1265 * a view hierarchy while skipping target views that should not be part of
1266 * the transition. For example, you may want to avoid animating children
1267 * of a specific ListView or Spinner. Views can be excluded either by their
1268 * id, or by their instance reference, or by the Class of that view
1269 * (eg, {@link Spinner}).</p>
1270 *
1271 * @see #excludeTarget(Class, boolean)
1272 * @see #excludeChildren(int, boolean)
1273 * @see #excludeChildren(View, boolean)
1274 *
1275 * @param type The type to ignore when running this transition.
1276 * @param exclude Whether to add the target type to or remove it from the
1277 * current list of excluded target types.
1278 * @return This transition object.
1279 */
1280 public Transition excludeChildren(Class type, boolean exclude) {
George Mount30da61d2014-05-09 13:17:52 -07001281 mTargetTypeChildExcludes = excludeObject(mTargetTypeChildExcludes, type, exclude);
Chet Haaseff58f922013-09-11 13:08:18 -07001282 return this;
1283 }
1284
1285 /**
Chet Haasefaebd8f2012-05-18 14:17:57 -07001286 * Sets the target view instances that this Transition is interested in
1287 * animating. By default, there are no targets, and a Transition will
1288 * listen for changes on every view in the hierarchy below the sceneRoot
1289 * of the Scene being transitioned into. Setting targets constrains
1290 * the Transition to only listen for, and act on, these views.
1291 * All other views will be ignored.
1292 *
Chet Haaseff58f922013-09-11 13:08:18 -07001293 * <p>The target list is like the {@link #addTarget(int) targetId}
Chet Haasefaebd8f2012-05-18 14:17:57 -07001294 * list except this list specifies the actual View instances, not the ids
1295 * of the views. This is an important distinction when scene changes involve
1296 * view hierarchies which have been inflated separately; different views may
1297 * share the same id but not actually be the same instance. If the transition
Chet Haaseff58f922013-09-11 13:08:18 -07001298 * should treat those views as the same, then {@link #addTarget(int)} should be used
Chet Haased82c8ac2013-08-26 14:20:16 -07001299 * instead of {@link #addTarget(View)}. If, on the other hand, scene changes involve
Chet Haasefaebd8f2012-05-18 14:17:57 -07001300 * changes all within the same view hierarchy, among views which do not
Chet Haased82c8ac2013-08-26 14:20:16 -07001301 * necessarily have ids set on them, then the target list of views may be more
Chet Haasefaebd8f2012-05-18 14:17:57 -07001302 * convenient.</p>
1303 *
Chet Haaseff58f922013-09-11 13:08:18 -07001304 * @see #addTarget(int)
Chet Haased82c8ac2013-08-26 14:20:16 -07001305 * @param target A View on which the Transition will act, must be non-null.
1306 * @return The Transition to which the target is added.
Chet Haasefaebd8f2012-05-18 14:17:57 -07001307 * Returning the same object makes it easier to chain calls during
1308 * construction, such as
Chet Haased82c8ac2013-08-26 14:20:16 -07001309 * <code>transitionSet.addTransitions(new Fade()).addTarget(someView);</code>
Chet Haasefaebd8f2012-05-18 14:17:57 -07001310 */
Chet Haased82c8ac2013-08-26 14:20:16 -07001311 public Transition addTarget(View target) {
1312 mTargets.add(target);
1313 return this;
1314 }
1315
1316 /**
1317 * Removes the given target from the list of targets that this Transition
1318 * is interested in animating.
1319 *
1320 * @param target The target view, must be non-null.
1321 * @return Transition The Transition from which the target is removed.
1322 * Returning the same object makes it easier to chain calls during
1323 * construction, such as
1324 * <code>transitionSet.addTransitions(new Fade()).removeTarget(someView);</code>
1325 */
1326 public Transition removeTarget(View target) {
1327 if (target != null) {
1328 mTargets.remove(target);
1329 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07001330 return this;
1331 }
1332
1333 /**
George Mount30da61d2014-05-09 13:17:52 -07001334 * Removes the given target from the list of targets that this Transition
1335 * is interested in animating.
1336 *
1337 * @param target The type of the target view, must be non-null.
1338 * @return Transition The Transition from which the target is removed.
1339 * Returning the same object makes it easier to chain calls during
1340 * construction, such as
1341 * <code>transitionSet.addTransitions(new Fade()).removeTarget(someType);</code>
1342 */
1343 public Transition removeTarget(Class target) {
1344 if (target != null) {
1345 mTargetTypes.remove(target);
1346 }
1347 return this;
1348 }
1349
1350 /**
1351 * Returns the list of target IDs that this transition limits itself to
1352 * tracking and animating. If the list is null or empty for
George Mount0a2ae002014-06-23 14:57:27 +00001353 * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
George Mount30da61d2014-05-09 13:17:52 -07001354 * {@link #getTargetTypes()} then this transition is
Chet Haasefaebd8f2012-05-18 14:17:57 -07001355 * not limited to specific views, and will handle changes to any views
1356 * in the hierarchy of a scene change.
1357 *
1358 * @return the list of target IDs
1359 */
Chet Haased82c8ac2013-08-26 14:20:16 -07001360 public List<Integer> getTargetIds() {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001361 return mTargetIds;
1362 }
1363
1364 /**
George Mount30da61d2014-05-09 13:17:52 -07001365 * Returns the list of target views that this transition limits itself to
1366 * tracking and animating. If the list is null or empty for
George Mount0a2ae002014-06-23 14:57:27 +00001367 * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
George Mount30da61d2014-05-09 13:17:52 -07001368 * {@link #getTargetTypes()} then this transition is
Chet Haasefaebd8f2012-05-18 14:17:57 -07001369 * not limited to specific views, and will handle changes to any views
1370 * in the hierarchy of a scene change.
1371 *
1372 * @return the list of target views
1373 */
Chet Haased82c8ac2013-08-26 14:20:16 -07001374 public List<View> getTargets() {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001375 return mTargets;
1376 }
1377
1378 /**
George Mount0a2ae002014-06-23 14:57:27 +00001379 * Returns the list of target transitionNames that this transition limits itself to
George Mount30da61d2014-05-09 13:17:52 -07001380 * tracking and animating. If the list is null or empty for
George Mount0a2ae002014-06-23 14:57:27 +00001381 * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
George Mount30da61d2014-05-09 13:17:52 -07001382 * {@link #getTargetTypes()} then this transition is
1383 * not limited to specific views, and will handle changes to any views
1384 * in the hierarchy of a scene change.
1385 *
George Mount0a2ae002014-06-23 14:57:27 +00001386 * @return the list of target transitionNames
1387 */
1388 public List<String> getTargetNames() {
1389 return mTargetNames;
1390 }
1391
1392 /**
1393 * To be removed before L release.
1394 * @hide
George Mount30da61d2014-05-09 13:17:52 -07001395 */
1396 public List<String> getTargetViewNames() {
1397 return mTargetNames;
1398 }
1399
1400 /**
George Mount0a2ae002014-06-23 14:57:27 +00001401 * Returns the list of target transitionNames that this transition limits itself to
George Mount30da61d2014-05-09 13:17:52 -07001402 * tracking and animating. If the list is null or empty for
George Mount0a2ae002014-06-23 14:57:27 +00001403 * {@link #getTargetIds()}, {@link #getTargets()}, {@link #getTargetNames()}, and
George Mount30da61d2014-05-09 13:17:52 -07001404 * {@link #getTargetTypes()} then this transition is
1405 * not limited to specific views, and will handle changes to any views
1406 * in the hierarchy of a scene change.
1407 *
1408 * @return the list of target Types
1409 */
1410 public List<Class> getTargetTypes() {
1411 return mTargetTypes;
1412 }
1413
1414 /**
Chet Haasefaebd8f2012-05-18 14:17:57 -07001415 * Recursive method that captures values for the given view and the
1416 * hierarchy underneath it.
1417 * @param sceneRoot The root of the view hierarchy being captured
1418 * @param start true if this capture is happening before the scene change,
1419 * false otherwise
1420 */
1421 void captureValues(ViewGroup sceneRoot, boolean start) {
Chet Haasedf32aa82013-10-21 17:19:37 -07001422 clearValues(start);
George Mount30da61d2014-05-09 13:17:52 -07001423 if ((mTargetIds.size() > 0 || mTargets.size() > 0)
1424 && (mTargetNames == null || mTargetNames.isEmpty())
1425 && (mTargetTypes == null || mTargetTypes.isEmpty())) {
1426 for (int i = 0; i < mTargetIds.size(); ++i) {
1427 int id = mTargetIds.get(i);
1428 View view = sceneRoot.findViewById(id);
1429 if (view != null) {
1430 TransitionValues values = new TransitionValues();
1431 values.view = view;
1432 if (start) {
1433 captureStartValues(values);
1434 } else {
1435 captureEndValues(values);
1436 }
George Mount5030c7f2014-09-10 10:58:08 -07001437 values.targetedTransitions.add(this);
George Mount30da61d2014-05-09 13:17:52 -07001438 capturePropagationValues(values);
1439 if (start) {
George Mount5030c7f2014-09-10 10:58:08 -07001440 addViewValues(mStartValues, view, values);
George Mount30da61d2014-05-09 13:17:52 -07001441 } else {
George Mount5030c7f2014-09-10 10:58:08 -07001442 addViewValues(mEndValues, view, values);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001443 }
1444 }
1445 }
George Mount30da61d2014-05-09 13:17:52 -07001446 for (int i = 0; i < mTargets.size(); ++i) {
1447 View view = mTargets.get(i);
1448 TransitionValues values = new TransitionValues();
1449 values.view = view;
1450 if (start) {
1451 captureStartValues(values);
1452 } else {
1453 captureEndValues(values);
1454 }
George Mount5030c7f2014-09-10 10:58:08 -07001455 values.targetedTransitions.add(this);
George Mount30da61d2014-05-09 13:17:52 -07001456 capturePropagationValues(values);
1457 if (start) {
George Mount2deeaa32014-09-19 10:02:02 -07001458 addViewValues(mStartValues, view, values);
George Mount30da61d2014-05-09 13:17:52 -07001459 } else {
George Mount2deeaa32014-09-19 10:02:02 -07001460 addViewValues(mEndValues, view, values);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001461 }
1462 }
1463 } else {
1464 captureHierarchy(sceneRoot, start);
1465 }
George Mountd4c3c912014-06-09 12:31:34 -07001466 if (!start && mNameOverrides != null) {
1467 int numOverrides = mNameOverrides.size();
1468 ArrayList<View> overriddenViews = new ArrayList<View>(numOverrides);
1469 for (int i = 0; i < numOverrides; i++) {
1470 String fromName = mNameOverrides.keyAt(i);
1471 overriddenViews.add(mStartValues.nameValues.remove(fromName));
1472 }
1473 for (int i = 0; i < numOverrides; i++) {
1474 View view = overriddenViews.get(i);
1475 if (view != null) {
1476 String toName = mNameOverrides.valueAt(i);
1477 mStartValues.nameValues.put(toName, view);
1478 }
1479 }
1480 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07001481 }
1482
George Mount30da61d2014-05-09 13:17:52 -07001483 static void addViewValues(TransitionValuesMaps transitionValuesMaps,
George Mount5030c7f2014-09-10 10:58:08 -07001484 View view, TransitionValues transitionValues) {
George Mount30da61d2014-05-09 13:17:52 -07001485 transitionValuesMaps.viewValues.put(view, transitionValues);
1486 int id = view.getId();
1487 if (id >= 0) {
1488 if (transitionValuesMaps.idValues.indexOfKey(id) >= 0) {
1489 // Duplicate IDs cannot match by ID.
1490 transitionValuesMaps.idValues.put(id, null);
1491 } else {
1492 transitionValuesMaps.idValues.put(id, view);
1493 }
1494 }
George Mount0a2ae002014-06-23 14:57:27 +00001495 String name = view.getTransitionName();
George Mount30da61d2014-05-09 13:17:52 -07001496 if (name != null) {
1497 if (transitionValuesMaps.nameValues.containsKey(name)) {
George Mount0a2ae002014-06-23 14:57:27 +00001498 // Duplicate transitionNames: cannot match by transitionName.
George Mount30da61d2014-05-09 13:17:52 -07001499 transitionValuesMaps.nameValues.put(name, null);
1500 } else {
1501 transitionValuesMaps.nameValues.put(name, view);
1502 }
1503 }
1504 if (view.getParent() instanceof ListView) {
1505 ListView listview = (ListView) view.getParent();
1506 if (listview.getAdapter().hasStableIds()) {
1507 int position = listview.getPositionForView(view);
1508 long itemId = listview.getItemIdAtPosition(position);
1509 if (transitionValuesMaps.itemIdValues.indexOfKey(itemId) >= 0) {
1510 // Duplicate item IDs: cannot match by item ID.
1511 View alreadyMatched = transitionValuesMaps.itemIdValues.get(itemId);
1512 if (alreadyMatched != null) {
George Mount5030c7f2014-09-10 10:58:08 -07001513 alreadyMatched.setHasTransientState(false);
George Mount30da61d2014-05-09 13:17:52 -07001514 transitionValuesMaps.itemIdValues.put(itemId, null);
1515 }
1516 } else {
George Mount5030c7f2014-09-10 10:58:08 -07001517 view.setHasTransientState(true);
George Mount30da61d2014-05-09 13:17:52 -07001518 transitionValuesMaps.itemIdValues.put(itemId, view);
1519 }
1520 }
1521 }
1522 }
1523
Chet Haasefaebd8f2012-05-18 14:17:57 -07001524 /**
Chet Haasedf32aa82013-10-21 17:19:37 -07001525 * Clear valuesMaps for specified start/end state
1526 *
1527 * @param start true if the start values should be cleared, false otherwise
1528 */
1529 void clearValues(boolean start) {
1530 if (start) {
1531 mStartValues.viewValues.clear();
1532 mStartValues.idValues.clear();
1533 mStartValues.itemIdValues.clear();
George Mountd4c3c912014-06-09 12:31:34 -07001534 mStartValues.nameValues.clear();
George Mount4d1ecf52014-07-29 11:15:54 -07001535 mStartValuesList = null;
Chet Haasedf32aa82013-10-21 17:19:37 -07001536 } else {
1537 mEndValues.viewValues.clear();
1538 mEndValues.idValues.clear();
1539 mEndValues.itemIdValues.clear();
George Mountd4c3c912014-06-09 12:31:34 -07001540 mEndValues.nameValues.clear();
George Mount4d1ecf52014-07-29 11:15:54 -07001541 mEndValuesList = null;
Chet Haasedf32aa82013-10-21 17:19:37 -07001542 }
1543 }
1544
1545 /**
Chet Haasefaebd8f2012-05-18 14:17:57 -07001546 * Recursive method which captures values for an entire view hierarchy,
1547 * starting at some root view. Transitions without targetIDs will use this
1548 * method to capture values for all possible views.
1549 *
1550 * @param view The view for which to capture values. Children of this View
1551 * will also be captured, recursively down to the leaf nodes.
1552 * @param start true if values are being captured in the start scene, false
1553 * otherwise.
1554 */
1555 private void captureHierarchy(View view, boolean start) {
1556 if (view == null) {
1557 return;
1558 }
George Mount30da61d2014-05-09 13:17:52 -07001559 int id = view.getId();
Chet Haaseff58f922013-09-11 13:08:18 -07001560 if (mTargetIdExcludes != null && mTargetIdExcludes.contains(id)) {
1561 return;
1562 }
1563 if (mTargetExcludes != null && mTargetExcludes.contains(view)) {
1564 return;
1565 }
1566 if (mTargetTypeExcludes != null && view != null) {
1567 int numTypes = mTargetTypeExcludes.size();
1568 for (int i = 0; i < numTypes; ++i) {
1569 if (mTargetTypeExcludes.get(i).isInstance(view)) {
1570 return;
1571 }
1572 }
1573 }
George Mounte1803372014-02-26 19:00:52 +00001574 if (view.getParent() instanceof ViewGroup) {
1575 TransitionValues values = new TransitionValues();
1576 values.view = view;
1577 if (start) {
1578 captureStartValues(values);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001579 } else {
George Mounte1803372014-02-26 19:00:52 +00001580 captureEndValues(values);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001581 }
George Mount5030c7f2014-09-10 10:58:08 -07001582 values.targetedTransitions.add(this);
George Mountd6107a32014-03-10 16:51:16 -07001583 capturePropagationValues(values);
George Mounte1803372014-02-26 19:00:52 +00001584 if (start) {
George Mount5030c7f2014-09-10 10:58:08 -07001585 addViewValues(mStartValues, view, values);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001586 } else {
George Mount5030c7f2014-09-10 10:58:08 -07001587 addViewValues(mEndValues, view, values);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001588 }
1589 }
1590 if (view instanceof ViewGroup) {
Chet Haaseff58f922013-09-11 13:08:18 -07001591 // Don't traverse child hierarchy if there are any child-excludes on this view
1592 if (mTargetIdChildExcludes != null && mTargetIdChildExcludes.contains(id)) {
1593 return;
1594 }
1595 if (mTargetChildExcludes != null && mTargetChildExcludes.contains(view)) {
1596 return;
1597 }
George Mount30da61d2014-05-09 13:17:52 -07001598 if (mTargetTypeChildExcludes != null) {
Chet Haaseff58f922013-09-11 13:08:18 -07001599 int numTypes = mTargetTypeChildExcludes.size();
1600 for (int i = 0; i < numTypes; ++i) {
1601 if (mTargetTypeChildExcludes.get(i).isInstance(view)) {
1602 return;
1603 }
1604 }
1605 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07001606 ViewGroup parent = (ViewGroup) view;
1607 for (int i = 0; i < parent.getChildCount(); ++i) {
1608 captureHierarchy(parent.getChildAt(i), start);
1609 }
1610 }
1611 }
1612
1613 /**
Chet Haase6ebe3de2013-06-17 16:50:50 -07001614 * This method can be called by transitions to get the TransitionValues for
1615 * any particular view during the transition-playing process. This might be
1616 * necessary, for example, to query the before/after state of related views
1617 * for a given transition.
1618 */
Chet Haased82c8ac2013-08-26 14:20:16 -07001619 public TransitionValues getTransitionValues(View view, boolean start) {
Chet Haase6ebe3de2013-06-17 16:50:50 -07001620 if (mParent != null) {
1621 return mParent.getTransitionValues(view, start);
1622 }
1623 TransitionValuesMaps valuesMaps = start ? mStartValues : mEndValues;
George Mount30da61d2014-05-09 13:17:52 -07001624 return valuesMaps.viewValues.get(view);
Chet Haase6ebe3de2013-06-17 16:50:50 -07001625 }
1626
1627 /**
George Mount4d1ecf52014-07-29 11:15:54 -07001628 * Find the matched start or end value for a given View. This is only valid
1629 * after playTransition starts. For example, it will be valid in
1630 * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)}, but not
1631 * in {@link #captureStartValues(TransitionValues)}.
1632 *
1633 * @param view The view to find the match for.
1634 * @param viewInStart Is View from the start values or end values.
1635 * @return The matching TransitionValues for view in either start or end values, depending
1636 * on viewInStart or null if there is no match for the given view.
1637 */
1638 TransitionValues getMatchedTransitionValues(View view, boolean viewInStart) {
1639 if (mParent != null) {
1640 return mParent.getMatchedTransitionValues(view, viewInStart);
1641 }
1642 ArrayList<TransitionValues> lookIn = viewInStart ? mStartValuesList : mEndValuesList;
1643 if (lookIn == null) {
1644 return null;
1645 }
1646 int count = lookIn.size();
1647 int index = -1;
1648 for (int i = 0; i < count; i++) {
1649 TransitionValues values = lookIn.get(i);
1650 if (values == null) {
George Mount1b4ae632015-05-07 10:18:43 -07001651 // Null values are always added to the end of the list, so we know to stop now.
George Mount4d1ecf52014-07-29 11:15:54 -07001652 return null;
1653 }
1654 if (values.view == view) {
1655 index = i;
1656 break;
1657 }
1658 }
1659 TransitionValues values = null;
1660 if (index >= 0) {
1661 ArrayList<TransitionValues> matchIn = viewInStart ? mEndValuesList : mStartValuesList;
1662 values = matchIn.get(index);
1663 }
1664 return values;
1665 }
1666
1667 /**
Chet Haase199acdf2013-07-24 18:40:55 -07001668 * Pauses this transition, sending out calls to {@link
1669 * TransitionListener#onTransitionPause(Transition)} to all listeners
1670 * and pausing all running animators started by this transition.
1671 *
1672 * @hide
1673 */
George Mountcf68aad2014-03-06 14:17:46 -08001674 public void pause(View sceneRoot) {
Chet Haasea56205c2013-09-10 11:30:22 -07001675 if (!mEnded) {
1676 ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
1677 int numOldAnims = runningAnimators.size();
George Mount51c01482014-07-24 14:24:44 -07001678 if (sceneRoot != null) {
1679 WindowId windowId = sceneRoot.getWindowId();
1680 for (int i = numOldAnims - 1; i >= 0; i--) {
1681 AnimationInfo info = runningAnimators.valueAt(i);
George Mount6276cd42014-08-28 16:17:38 -07001682 if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
George Mount51c01482014-07-24 14:24:44 -07001683 Animator anim = runningAnimators.keyAt(i);
1684 anim.pause();
1685 }
George Mountcf68aad2014-03-06 14:17:46 -08001686 }
Chet Haase199acdf2013-07-24 18:40:55 -07001687 }
Chet Haasea56205c2013-09-10 11:30:22 -07001688 if (mListeners != null && mListeners.size() > 0) {
1689 ArrayList<TransitionListener> tmpListeners =
1690 (ArrayList<TransitionListener>) mListeners.clone();
1691 int numListeners = tmpListeners.size();
1692 for (int i = 0; i < numListeners; ++i) {
1693 tmpListeners.get(i).onTransitionPause(this);
1694 }
1695 }
1696 mPaused = true;
Chet Haase199acdf2013-07-24 18:40:55 -07001697 }
Chet Haase199acdf2013-07-24 18:40:55 -07001698 }
1699
1700 /**
1701 * Resumes this transition, sending out calls to {@link
1702 * TransitionListener#onTransitionPause(Transition)} to all listeners
1703 * and pausing all running animators started by this transition.
1704 *
1705 * @hide
1706 */
George Mountcf68aad2014-03-06 14:17:46 -08001707 public void resume(View sceneRoot) {
Chet Haase199acdf2013-07-24 18:40:55 -07001708 if (mPaused) {
Chet Haasea56205c2013-09-10 11:30:22 -07001709 if (!mEnded) {
1710 ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
1711 int numOldAnims = runningAnimators.size();
George Mountcf68aad2014-03-06 14:17:46 -08001712 WindowId windowId = sceneRoot.getWindowId();
Chet Haasea56205c2013-09-10 11:30:22 -07001713 for (int i = numOldAnims - 1; i >= 0; i--) {
George Mountcf68aad2014-03-06 14:17:46 -08001714 AnimationInfo info = runningAnimators.valueAt(i);
George Mount6276cd42014-08-28 16:17:38 -07001715 if (info.view != null && windowId != null && windowId.equals(info.windowId)) {
George Mountcf68aad2014-03-06 14:17:46 -08001716 Animator anim = runningAnimators.keyAt(i);
1717 anim.resume();
1718 }
Chet Haasea56205c2013-09-10 11:30:22 -07001719 }
1720 if (mListeners != null && mListeners.size() > 0) {
1721 ArrayList<TransitionListener> tmpListeners =
1722 (ArrayList<TransitionListener>) mListeners.clone();
1723 int numListeners = tmpListeners.size();
1724 for (int i = 0; i < numListeners; ++i) {
1725 tmpListeners.get(i).onTransitionResume(this);
1726 }
Chet Haase199acdf2013-07-24 18:40:55 -07001727 }
1728 }
1729 mPaused = false;
1730 }
1731 }
1732
1733 /**
Chet Haasefaebd8f2012-05-18 14:17:57 -07001734 * Called by TransitionManager to play the transition. This calls
Chet Haased82c8ac2013-08-26 14:20:16 -07001735 * createAnimators() to set things up and create all of the animations and then
Chet Haase2ea7f8b2013-06-21 15:00:05 -07001736 * runAnimations() to actually start the animations.
Chet Haasefaebd8f2012-05-18 14:17:57 -07001737 */
Chet Haase6ebe3de2013-06-17 16:50:50 -07001738 void playTransition(ViewGroup sceneRoot) {
George Mount4d1ecf52014-07-29 11:15:54 -07001739 mStartValuesList = new ArrayList<TransitionValues>();
1740 mEndValuesList = new ArrayList<TransitionValues>();
1741 matchStartAndEnd(mStartValues, mEndValues);
1742
Chet Haase199acdf2013-07-24 18:40:55 -07001743 ArrayMap<Animator, AnimationInfo> runningAnimators = getRunningAnimators();
1744 int numOldAnims = runningAnimators.size();
George Mountfe361d22014-07-08 17:25:25 -07001745 WindowId windowId = sceneRoot.getWindowId();
Chet Haase199acdf2013-07-24 18:40:55 -07001746 for (int i = numOldAnims - 1; i >= 0; i--) {
1747 Animator anim = runningAnimators.keyAt(i);
1748 if (anim != null) {
Chet Haase199acdf2013-07-24 18:40:55 -07001749 AnimationInfo oldInfo = runningAnimators.get(anim);
George Mountfe361d22014-07-08 17:25:25 -07001750 if (oldInfo != null && oldInfo.view != null && oldInfo.windowId == windowId) {
Chet Haase199acdf2013-07-24 18:40:55 -07001751 TransitionValues oldValues = oldInfo.values;
1752 View oldView = oldInfo.view;
George Mount5030c7f2014-09-10 10:58:08 -07001753 TransitionValues startValues = getTransitionValues(oldView, true);
1754 TransitionValues endValues = getMatchedTransitionValues(oldView, true);
George Mount1b4ae632015-05-07 10:18:43 -07001755 if (startValues == null && endValues == null) {
1756 endValues = mEndValues.viewValues.get(oldView);
1757 }
George Mount5030c7f2014-09-10 10:58:08 -07001758 boolean cancel = (startValues != null || endValues != null) &&
George Mountaef1ae32015-06-04 12:51:55 -07001759 oldInfo.transition.isTransitionRequired(oldValues, endValues);
Chet Haase199acdf2013-07-24 18:40:55 -07001760 if (cancel) {
1761 if (anim.isRunning() || anim.isStarted()) {
1762 if (DBG) {
1763 Log.d(LOG_TAG, "Canceling anim " + anim);
1764 }
1765 anim.cancel();
1766 } else {
1767 if (DBG) {
1768 Log.d(LOG_TAG, "removing anim from info list: " + anim);
1769 }
1770 runningAnimators.remove(anim);
1771 }
1772 }
1773 }
1774 }
1775 }
1776
George Mount4d1ecf52014-07-29 11:15:54 -07001777 createAnimators(sceneRoot, mStartValues, mEndValues, mStartValuesList, mEndValuesList);
Chet Haased82c8ac2013-08-26 14:20:16 -07001778 runAnimators();
Chet Haasefaebd8f2012-05-18 14:17:57 -07001779 }
1780
Todd Volkert0653f2592015-02-12 15:27:57 -08001781 /**
George Mountaef1ae32015-06-04 12:51:55 -07001782 * Returns whether or not the transition should create an Animator, based on the values
1783 * captured during {@link #captureStartValues(TransitionValues)} and
1784 * {@link #captureEndValues(TransitionValues)}. The default implementation compares the
Todd Volkert0653f2592015-02-12 15:27:57 -08001785 * property values returned from {@link #getTransitionProperties()}, or all property values if
1786 * {@code getTransitionProperties()} returns null. Subclasses may override this method to
George Mountaef1ae32015-06-04 12:51:55 -07001787 * provide logic more specific to the transition implementation.
Todd Volkert0653f2592015-02-12 15:27:57 -08001788 *
George Mountaef1ae32015-06-04 12:51:55 -07001789 * @param startValues the values from captureStartValues, This may be {@code null} if the
1790 * View did not exist in the start state.
1791 * @param endValues the values from captureEndValues. This may be {@code null} if the View
1792 * did not exist in the end state.
Todd Volkert0653f2592015-02-12 15:27:57 -08001793 */
George Mountaef1ae32015-06-04 12:51:55 -07001794 public boolean isTransitionRequired(@Nullable TransitionValues startValues,
1795 @Nullable TransitionValues endValues) {
George Mount4c20ea22014-06-17 10:14:39 -07001796 boolean valuesChanged = false;
George Mountaef1ae32015-06-04 12:51:55 -07001797 // if startValues null, then transition didn't care to stash values,
George Mount4c20ea22014-06-17 10:14:39 -07001798 // and won't get canceled
George Mountaef1ae32015-06-04 12:51:55 -07001799 if (startValues != null && endValues != null) {
Dake Guc94e2b32014-07-22 14:53:53 -07001800 String[] properties = getTransitionProperties();
1801 if (properties != null) {
1802 int count = properties.length;
1803 for (int i = 0; i < count; i++) {
George Mountaef1ae32015-06-04 12:51:55 -07001804 if (isValueChanged(startValues, endValues, properties[i])) {
Dake Guc94e2b32014-07-22 14:53:53 -07001805 valuesChanged = true;
1806 break;
George Mount4c20ea22014-06-17 10:14:39 -07001807 }
Dake Guc94e2b32014-07-22 14:53:53 -07001808 }
1809 } else {
George Mountaef1ae32015-06-04 12:51:55 -07001810 for (String key : startValues.values.keySet()) {
1811 if (isValueChanged(startValues, endValues, key)) {
Dake Guc94e2b32014-07-22 14:53:53 -07001812 valuesChanged = true;
1813 break;
1814 }
George Mount4c20ea22014-06-17 10:14:39 -07001815 }
1816 }
1817 }
1818 return valuesChanged;
1819 }
1820
Dake Guc94e2b32014-07-22 14:53:53 -07001821 private static boolean isValueChanged(TransitionValues oldValues, TransitionValues newValues,
1822 String key) {
George Mountcba01b22014-10-22 14:39:43 -07001823 if (oldValues.values.containsKey(key) != newValues.values.containsKey(key)) {
1824 // The transition didn't care about this particular value, so we don't care, either.
1825 return false;
1826 }
Dake Guc94e2b32014-07-22 14:53:53 -07001827 Object oldValue = oldValues.values.get(key);
1828 Object newValue = newValues.values.get(key);
George Mount5030c7f2014-09-10 10:58:08 -07001829 boolean changed;
1830 if (oldValue == null && newValue == null) {
1831 // both are null
1832 changed = false;
1833 } else if (oldValue == null || newValue == null) {
1834 // one is null
1835 changed = true;
1836 } else {
1837 // neither is null
1838 changed = !oldValue.equals(newValue);
1839 }
Dake Guc94e2b32014-07-22 14:53:53 -07001840 if (DBG && changed) {
1841 Log.d(LOG_TAG, "Transition.playTransition: " +
1842 "oldValue != newValue for " + key +
1843 ": old, new = " + oldValue + ", " + newValue);
1844 }
1845 return changed;
1846 }
1847
Chet Haasefaebd8f2012-05-18 14:17:57 -07001848 /**
1849 * This is a utility method used by subclasses to handle standard parts of
1850 * setting up and running an Animator: it sets the {@link #getDuration()
1851 * duration} and the {@link #getStartDelay() startDelay}, starts the
Chet Haase199acdf2013-07-24 18:40:55 -07001852 * animation, and, when the animator ends, calls {@link #end()}.
Chet Haasefaebd8f2012-05-18 14:17:57 -07001853 *
1854 * @param animator The Animator to be run during this transition.
1855 *
1856 * @hide
1857 */
1858 protected void animate(Animator animator) {
1859 // TODO: maybe pass auto-end as a boolean parameter?
1860 if (animator == null) {
Chet Haase199acdf2013-07-24 18:40:55 -07001861 end();
Chet Haasefaebd8f2012-05-18 14:17:57 -07001862 } else {
1863 if (getDuration() >= 0) {
1864 animator.setDuration(getDuration());
1865 }
1866 if (getStartDelay() >= 0) {
George Mountd6107a32014-03-10 16:51:16 -07001867 animator.setStartDelay(getStartDelay() + animator.getStartDelay());
Chet Haasefaebd8f2012-05-18 14:17:57 -07001868 }
1869 if (getInterpolator() != null) {
1870 animator.setInterpolator(getInterpolator());
1871 }
1872 animator.addListener(new AnimatorListenerAdapter() {
1873 @Override
Chet Haasefaebd8f2012-05-18 14:17:57 -07001874 public void onAnimationEnd(Animator animation) {
Chet Haase199acdf2013-07-24 18:40:55 -07001875 end();
Chet Haasefaebd8f2012-05-18 14:17:57 -07001876 animation.removeListener(this);
1877 }
1878 });
1879 animator.start();
1880 }
1881 }
1882
1883 /**
Chet Haasefaebd8f2012-05-18 14:17:57 -07001884 * This method is called automatically by the transition and
Chet Haased82c8ac2013-08-26 14:20:16 -07001885 * TransitionSet classes prior to a Transition subclass starting;
Chet Haasefaebd8f2012-05-18 14:17:57 -07001886 * subclasses should not need to call it directly.
1887 *
1888 * @hide
1889 */
Chet Haase199acdf2013-07-24 18:40:55 -07001890 protected void start() {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001891 if (mNumInstances == 0) {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001892 if (mListeners != null && mListeners.size() > 0) {
1893 ArrayList<TransitionListener> tmpListeners =
1894 (ArrayList<TransitionListener>) mListeners.clone();
1895 int numListeners = tmpListeners.size();
1896 for (int i = 0; i < numListeners; ++i) {
1897 tmpListeners.get(i).onTransitionStart(this);
1898 }
1899 }
Chet Haasea56205c2013-09-10 11:30:22 -07001900 mEnded = false;
Chet Haasefaebd8f2012-05-18 14:17:57 -07001901 }
1902 mNumInstances++;
1903 }
1904
1905 /**
1906 * This method is called automatically by the Transition and
Chet Haased82c8ac2013-08-26 14:20:16 -07001907 * TransitionSet classes when a transition finishes, either because
Chet Haasefaebd8f2012-05-18 14:17:57 -07001908 * a transition did nothing (returned a null Animator from
Chet Haased82c8ac2013-08-26 14:20:16 -07001909 * {@link Transition#createAnimator(ViewGroup, TransitionValues,
Chet Haasefaebd8f2012-05-18 14:17:57 -07001910 * TransitionValues)}) or because the transition returned a valid
Chet Haase199acdf2013-07-24 18:40:55 -07001911 * Animator and end() was called in the onAnimationEnd()
Chet Haasefaebd8f2012-05-18 14:17:57 -07001912 * callback of the AnimatorListener.
1913 *
1914 * @hide
1915 */
Chet Haase199acdf2013-07-24 18:40:55 -07001916 protected void end() {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001917 --mNumInstances;
1918 if (mNumInstances == 0) {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001919 if (mListeners != null && mListeners.size() > 0) {
1920 ArrayList<TransitionListener> tmpListeners =
1921 (ArrayList<TransitionListener>) mListeners.clone();
1922 int numListeners = tmpListeners.size();
1923 for (int i = 0; i < numListeners; ++i) {
1924 tmpListeners.get(i).onTransitionEnd(this);
1925 }
1926 }
Chet Haase6ebe3de2013-06-17 16:50:50 -07001927 for (int i = 0; i < mStartValues.itemIdValues.size(); ++i) {
George Mount30da61d2014-05-09 13:17:52 -07001928 View view = mStartValues.itemIdValues.valueAt(i);
1929 if (view != null) {
1930 view.setHasTransientState(false);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001931 }
1932 }
Chet Haase6ebe3de2013-06-17 16:50:50 -07001933 for (int i = 0; i < mEndValues.itemIdValues.size(); ++i) {
George Mount30da61d2014-05-09 13:17:52 -07001934 View view = mEndValues.itemIdValues.valueAt(i);
1935 if (view != null) {
1936 view.setHasTransientState(false);
Chet Haasefaebd8f2012-05-18 14:17:57 -07001937 }
1938 }
Chet Haasea56205c2013-09-10 11:30:22 -07001939 mEnded = true;
Chet Haasefaebd8f2012-05-18 14:17:57 -07001940 }
1941 }
1942
1943 /**
1944 * This method cancels a transition that is currently running.
Chet Haased82c8ac2013-08-26 14:20:16 -07001945 *
1946 * @hide
Chet Haasefaebd8f2012-05-18 14:17:57 -07001947 */
Chet Haase199acdf2013-07-24 18:40:55 -07001948 protected void cancel() {
Chet Haasee9d32ea2013-06-04 08:46:42 -07001949 int numAnimators = mCurrentAnimators.size();
Chet Haase25a738f2013-06-04 16:35:14 -07001950 for (int i = numAnimators - 1; i >= 0; i--) {
Chet Haasee9d32ea2013-06-04 08:46:42 -07001951 Animator animator = mCurrentAnimators.get(i);
1952 animator.cancel();
1953 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07001954 if (mListeners != null && mListeners.size() > 0) {
1955 ArrayList<TransitionListener> tmpListeners =
1956 (ArrayList<TransitionListener>) mListeners.clone();
1957 int numListeners = tmpListeners.size();
1958 for (int i = 0; i < numListeners; ++i) {
1959 tmpListeners.get(i).onTransitionCancel(this);
1960 }
1961 }
1962 }
1963
1964 /**
1965 * Adds a listener to the set of listeners that are sent events through the
1966 * life of an animation, such as start, repeat, and end.
1967 *
1968 * @param listener the listener to be added to the current set of listeners
1969 * for this animation.
Chet Haased82c8ac2013-08-26 14:20:16 -07001970 * @return This transition object.
Chet Haasefaebd8f2012-05-18 14:17:57 -07001971 */
Chet Haased82c8ac2013-08-26 14:20:16 -07001972 public Transition addListener(TransitionListener listener) {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001973 if (mListeners == null) {
1974 mListeners = new ArrayList<TransitionListener>();
1975 }
1976 mListeners.add(listener);
Chet Haased82c8ac2013-08-26 14:20:16 -07001977 return this;
Chet Haasefaebd8f2012-05-18 14:17:57 -07001978 }
1979
1980 /**
1981 * Removes a listener from the set listening to this animation.
1982 *
1983 * @param listener the listener to be removed from the current set of
1984 * listeners for this transition.
Chet Haased82c8ac2013-08-26 14:20:16 -07001985 * @return This transition object.
Chet Haasefaebd8f2012-05-18 14:17:57 -07001986 */
Chet Haased82c8ac2013-08-26 14:20:16 -07001987 public Transition removeListener(TransitionListener listener) {
Chet Haasefaebd8f2012-05-18 14:17:57 -07001988 if (mListeners == null) {
Chet Haased82c8ac2013-08-26 14:20:16 -07001989 return this;
Chet Haasefaebd8f2012-05-18 14:17:57 -07001990 }
1991 mListeners.remove(listener);
1992 if (mListeners.size() == 0) {
1993 mListeners = null;
1994 }
Chet Haased82c8ac2013-08-26 14:20:16 -07001995 return this;
Chet Haasefaebd8f2012-05-18 14:17:57 -07001996 }
1997
George Mountd6107a32014-03-10 16:51:16 -07001998 /**
1999 * Sets the callback to use to find the epicenter of a Transition. A null value indicates
George Mountdc21d3b2014-06-05 09:42:48 -07002000 * that there is no epicenter in the Transition and onGetEpicenter() will return null.
George Mountd6107a32014-03-10 16:51:16 -07002001 * Transitions like {@link android.transition.Explode} use a point or Rect to orient
2002 * the direction of travel. This is called the epicenter of the Transition and is
2003 * typically centered on a touched View. The
2004 * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
2005 * dynamically retrieve the epicenter during a Transition.
2006 * @param epicenterCallback The callback to use to find the epicenter of the Transition.
2007 */
2008 public void setEpicenterCallback(EpicenterCallback epicenterCallback) {
2009 mEpicenterCallback = epicenterCallback;
2010 }
2011
2012 /**
2013 * Returns the callback used to find the epicenter of the Transition.
2014 * Transitions like {@link android.transition.Explode} use a point or Rect to orient
2015 * the direction of travel. This is called the epicenter of the Transition and is
2016 * typically centered on a touched View. The
2017 * {@link android.transition.Transition.EpicenterCallback} allows a Transition to
2018 * dynamically retrieve the epicenter during a Transition.
2019 * @return the callback used to find the epicenter of the Transition.
2020 */
2021 public EpicenterCallback getEpicenterCallback() {
2022 return mEpicenterCallback;
2023 }
2024
2025 /**
2026 * Returns the epicenter as specified by the
2027 * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
2028 * @return the epicenter as specified by the
2029 * {@link android.transition.Transition.EpicenterCallback} or null if no callback exists.
2030 * @see #setEpicenterCallback(android.transition.Transition.EpicenterCallback)
2031 */
2032 public Rect getEpicenter() {
2033 if (mEpicenterCallback == null) {
2034 return null;
2035 }
George Mountdc21d3b2014-06-05 09:42:48 -07002036 return mEpicenterCallback.onGetEpicenter(this);
George Mountd6107a32014-03-10 16:51:16 -07002037 }
2038
2039 /**
George Mountecd857b2014-06-19 07:51:08 -07002040 * Sets the algorithm used to calculate two-dimensional interpolation.
2041 * <p>
2042 * Transitions such as {@link android.transition.ChangeBounds} move Views, typically
2043 * in a straight path between the start and end positions. Applications that desire to
2044 * have these motions move in a curve can change how Views interpolate in two dimensions
2045 * by extending PathMotion and implementing
2046 * {@link android.transition.PathMotion#getPath(float, float, float, float)}.
2047 * </p>
George Mountf9557612014-08-29 11:32:13 -07002048 * <p>
2049 * When describing in XML, use a nested XML tag for the path motion. It can be one of
2050 * the built-in tags <code>arcMotion</code> or <code>patternPathMotion</code> or it can
2051 * be a custom PathMotion using <code>pathMotion</code> with the <code>class</code>
2052 * attributed with the fully-described class name. For example:</p>
2053 * <pre>
2054 * {@code
Neil Fuller71fbb812015-11-30 09:51:33 +00002055 * <changeBounds>
2056 * <pathMotion class="my.app.transition.MyPathMotion"/>
2057 * </changeBounds>
George Mountf9557612014-08-29 11:32:13 -07002058 * }
2059 * </pre>
2060 * <p>or</p>
2061 * <pre>
2062 * {@code
Neil Fuller71fbb812015-11-30 09:51:33 +00002063 * <changeBounds>
2064 * <arcMotion android:minimumHorizontalAngle="15"
George Mountf9557612014-08-29 11:32:13 -07002065 * android:minimumVerticalAngle="0" android:maximumAngle="90"/>
Neil Fuller71fbb812015-11-30 09:51:33 +00002066 * </changeBounds>
George Mountf9557612014-08-29 11:32:13 -07002067 * }
2068 * </pre>
2069 *
George Mountecd857b2014-06-19 07:51:08 -07002070 * @param pathMotion Algorithm object to use for determining how to interpolate in two
2071 * dimensions. If null, a straight-path algorithm will be used.
George Mountf9557612014-08-29 11:32:13 -07002072 * @see android.transition.ArcMotion
2073 * @see PatternPathMotion
2074 * @see android.transition.PathMotion
George Mountecd857b2014-06-19 07:51:08 -07002075 */
2076 public void setPathMotion(PathMotion pathMotion) {
2077 if (pathMotion == null) {
2078 mPathMotion = STRAIGHT_PATH_MOTION;
2079 } else {
2080 mPathMotion = pathMotion;
2081 }
2082 }
2083
2084 /**
2085 * Returns the algorithm object used to interpolate along two dimensions. This is typically
2086 * used to determine the View motion between two points.
2087 *
George Mountf9557612014-08-29 11:32:13 -07002088 * <p>
2089 * When describing in XML, use a nested XML tag for the path motion. It can be one of
2090 * the built-in tags <code>arcMotion</code> or <code>patternPathMotion</code> or it can
2091 * be a custom PathMotion using <code>pathMotion</code> with the <code>class</code>
2092 * attributed with the fully-described class name. For example:</p>
Neil Fuller71fbb812015-11-30 09:51:33 +00002093 * <pre>{@code
2094 * <changeBounds>
2095 * <pathMotion class="my.app.transition.MyPathMotion"/>
2096 * </changeBounds>}
George Mountf9557612014-08-29 11:32:13 -07002097 * </pre>
2098 * <p>or</p>
Neil Fuller71fbb812015-11-30 09:51:33 +00002099 * <pre>{@code
2100 * <changeBounds>
2101 * <arcMotion android:minimumHorizontalAngle="15"
George Mountf9557612014-08-29 11:32:13 -07002102 * android:minimumVerticalAngle="0"
2103 * android:maximumAngle="90"/>
Neil Fuller71fbb812015-11-30 09:51:33 +00002104 * </changeBounds>}
George Mountf9557612014-08-29 11:32:13 -07002105 * </pre>
2106 *
George Mountecd857b2014-06-19 07:51:08 -07002107 * @return The algorithm object used to interpolate along two dimensions.
George Mountf9557612014-08-29 11:32:13 -07002108 * @see android.transition.ArcMotion
2109 * @see PatternPathMotion
2110 * @see android.transition.PathMotion
George Mountecd857b2014-06-19 07:51:08 -07002111 */
2112 public PathMotion getPathMotion() {
2113 return mPathMotion;
2114 }
2115
2116 /**
George Mountd6107a32014-03-10 16:51:16 -07002117 * Sets the method for determining Animator start delays.
2118 * When a Transition affects several Views like {@link android.transition.Explode} or
2119 * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
2120 * such that the Animator start delay depends on position of the View. The
2121 * TransitionPropagation specifies how the start delays are calculated.
2122 * @param transitionPropagation The class used to determine the start delay of
2123 * Animators created by this Transition. A null value
2124 * indicates that no delay should be used.
2125 */
2126 public void setPropagation(TransitionPropagation transitionPropagation) {
2127 mPropagation = transitionPropagation;
2128 }
2129
2130 /**
2131 * Returns the {@link android.transition.TransitionPropagation} used to calculate Animator start
2132 * delays.
2133 * When a Transition affects several Views like {@link android.transition.Explode} or
2134 * {@link android.transition.Slide}, there may be a desire to have a "wave-front" effect
2135 * such that the Animator start delay depends on position of the View. The
2136 * TransitionPropagation specifies how the start delays are calculated.
2137 * @return the {@link android.transition.TransitionPropagation} used to calculate Animator start
2138 * delays. This is null by default.
2139 */
2140 public TransitionPropagation getPropagation() {
2141 return mPropagation;
2142 }
2143
2144 /**
2145 * Captures TransitionPropagation values for the given view and the
2146 * hierarchy underneath it.
2147 */
2148 void capturePropagationValues(TransitionValues transitionValues) {
George Mount31a21722014-03-24 17:44:36 -07002149 if (mPropagation != null && !transitionValues.values.isEmpty()) {
George Mountd6107a32014-03-10 16:51:16 -07002150 String[] propertyNames = mPropagation.getPropagationProperties();
2151 if (propertyNames == null) {
2152 return;
2153 }
2154 boolean containsAll = true;
2155 for (int i = 0; i < propertyNames.length; i++) {
2156 if (!transitionValues.values.containsKey(propertyNames[i])) {
2157 containsAll = false;
2158 break;
2159 }
2160 }
2161 if (!containsAll) {
2162 mPropagation.captureValues(transitionValues);
2163 }
2164 }
2165 }
2166
Chet Haased82c8ac2013-08-26 14:20:16 -07002167 Transition setSceneRoot(ViewGroup sceneRoot) {
Chet Haase6ebe3de2013-06-17 16:50:50 -07002168 mSceneRoot = sceneRoot;
Chet Haased82c8ac2013-08-26 14:20:16 -07002169 return this;
Chet Haase6ebe3de2013-06-17 16:50:50 -07002170 }
2171
Chet Haaseb7a7fc92013-09-20 16:33:08 -07002172 void setCanRemoveViews(boolean canRemoveViews) {
2173 mCanRemoveViews = canRemoveViews;
2174 }
2175
George Mount0a778ed2013-12-13 13:35:36 -08002176 public boolean canRemoveViews() {
2177 return mCanRemoveViews;
2178 }
2179
George Mountd4c3c912014-06-09 12:31:34 -07002180 /**
2181 * Sets the shared element names -- a mapping from a name at the start state to
2182 * a different name at the end state.
2183 * @hide
2184 */
2185 public void setNameOverrides(ArrayMap<String, String> overrides) {
2186 mNameOverrides = overrides;
2187 }
2188
2189 /** @hide */
2190 public ArrayMap<String, String> getNameOverrides() {
2191 return mNameOverrides;
2192 }
2193
Chet Haasefaebd8f2012-05-18 14:17:57 -07002194 @Override
2195 public String toString() {
2196 return toString("");
2197 }
2198
Chet Haase6ebe3de2013-06-17 16:50:50 -07002199 @Override
2200 public Transition clone() {
2201 Transition clone = null;
2202 try {
2203 clone = (Transition) super.clone();
Chet Haase199acdf2013-07-24 18:40:55 -07002204 clone.mAnimators = new ArrayList<Animator>();
Chet Haase7660d122013-09-13 13:29:31 -07002205 clone.mStartValues = new TransitionValuesMaps();
2206 clone.mEndValues = new TransitionValuesMaps();
George Mount4d1ecf52014-07-29 11:15:54 -07002207 clone.mStartValuesList = null;
2208 clone.mEndValuesList = null;
Chet Haase6ebe3de2013-06-17 16:50:50 -07002209 } catch (CloneNotSupportedException e) {}
2210
2211 return clone;
2212 }
2213
Chet Haase199acdf2013-07-24 18:40:55 -07002214 /**
2215 * Returns the name of this Transition. This name is used internally to distinguish
2216 * between different transitions to determine when interrupting transitions overlap.
Chet Haased82c8ac2013-08-26 14:20:16 -07002217 * For example, a ChangeBounds running on the same target view as another ChangeBounds
2218 * should determine whether the old transition is animating to different end values
2219 * and should be canceled in favor of the new transition.
Chet Haase199acdf2013-07-24 18:40:55 -07002220 *
2221 * <p>By default, a Transition's name is simply the value of {@link Class#getName()},
2222 * but subclasses are free to override and return something different.</p>
2223 *
2224 * @return The name of this transition.
2225 */
2226 public String getName() {
2227 return mName;
2228 }
2229
Chet Haasefaebd8f2012-05-18 14:17:57 -07002230 String toString(String indent) {
2231 String result = indent + getClass().getSimpleName() + "@" +
2232 Integer.toHexString(hashCode()) + ": ";
Chet Haasec43524f2013-07-16 14:40:11 -07002233 if (mDuration != -1) {
2234 result += "dur(" + mDuration + ") ";
Chet Haasefaebd8f2012-05-18 14:17:57 -07002235 }
Chet Haasec43524f2013-07-16 14:40:11 -07002236 if (mStartDelay != -1) {
2237 result += "dly(" + mStartDelay + ") ";
Chet Haasefaebd8f2012-05-18 14:17:57 -07002238 }
Chet Haasec43524f2013-07-16 14:40:11 -07002239 if (mInterpolator != null) {
2240 result += "interp(" + mInterpolator + ") ";
2241 }
Chet Haased82c8ac2013-08-26 14:20:16 -07002242 if (mTargetIds.size() > 0 || mTargets.size() > 0) {
Chet Haasec43524f2013-07-16 14:40:11 -07002243 result += "tgts(";
Chet Haased82c8ac2013-08-26 14:20:16 -07002244 if (mTargetIds.size() > 0) {
2245 for (int i = 0; i < mTargetIds.size(); ++i) {
Chet Haasec43524f2013-07-16 14:40:11 -07002246 if (i > 0) {
2247 result += ", ";
2248 }
Chet Haased82c8ac2013-08-26 14:20:16 -07002249 result += mTargetIds.get(i);
Chet Haasec43524f2013-07-16 14:40:11 -07002250 }
2251 }
Chet Haased82c8ac2013-08-26 14:20:16 -07002252 if (mTargets.size() > 0) {
2253 for (int i = 0; i < mTargets.size(); ++i) {
Chet Haasec43524f2013-07-16 14:40:11 -07002254 if (i > 0) {
2255 result += ", ";
2256 }
Chet Haased82c8ac2013-08-26 14:20:16 -07002257 result += mTargets.get(i);
Chet Haasec43524f2013-07-16 14:40:11 -07002258 }
2259 }
2260 result += ")";
2261 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07002262 return result;
2263 }
2264
2265 /**
2266 * A transition listener receives notifications from a transition.
Chet Haase199acdf2013-07-24 18:40:55 -07002267 * Notifications indicate transition lifecycle events.
Chet Haasefaebd8f2012-05-18 14:17:57 -07002268 */
2269 public static interface TransitionListener {
2270 /**
2271 * Notification about the start of the transition.
2272 *
2273 * @param transition The started transition.
2274 */
2275 void onTransitionStart(Transition transition);
2276
2277 /**
2278 * Notification about the end of the transition. Canceled transitions
2279 * will always notify listeners of both the cancellation and end
Chet Haase199acdf2013-07-24 18:40:55 -07002280 * events. That is, {@link #onTransitionEnd(Transition)} is always called,
Chet Haasefaebd8f2012-05-18 14:17:57 -07002281 * regardless of whether the transition was canceled or played
2282 * through to completion.
2283 *
2284 * @param transition The transition which reached its end.
2285 */
2286 void onTransitionEnd(Transition transition);
2287
2288 /**
2289 * Notification about the cancellation of the transition.
Chet Haased82c8ac2013-08-26 14:20:16 -07002290 * Note that cancel may be called by a parent {@link TransitionSet} on
Chet Haase199acdf2013-07-24 18:40:55 -07002291 * a child transition which has not yet started. This allows the child
2292 * transition to restore state on target objects which was set at
Chet Haased82c8ac2013-08-26 14:20:16 -07002293 * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)
2294 * createAnimator()} time.
Chet Haasefaebd8f2012-05-18 14:17:57 -07002295 *
2296 * @param transition The transition which was canceled.
2297 */
2298 void onTransitionCancel(Transition transition);
Chet Haase199acdf2013-07-24 18:40:55 -07002299
2300 /**
2301 * Notification when a transition is paused.
Chet Haased82c8ac2013-08-26 14:20:16 -07002302 * Note that createAnimator() may be called by a parent {@link TransitionSet} on
Chet Haase199acdf2013-07-24 18:40:55 -07002303 * a child transition which has not yet started. This allows the child
2304 * transition to restore state on target objects which was set at
Chet Haased82c8ac2013-08-26 14:20:16 -07002305 * {@link #createAnimator(android.view.ViewGroup, TransitionValues, TransitionValues)
2306 * createAnimator()} time.
Chet Haase199acdf2013-07-24 18:40:55 -07002307 *
2308 * @param transition The transition which was paused.
2309 */
2310 void onTransitionPause(Transition transition);
2311
2312 /**
2313 * Notification when a transition is resumed.
Chet Haased82c8ac2013-08-26 14:20:16 -07002314 * Note that resume() may be called by a parent {@link TransitionSet} on
Chet Haase199acdf2013-07-24 18:40:55 -07002315 * a child transition which has not yet started. This allows the child
2316 * transition to restore state which may have changed in an earlier call
2317 * to {@link #onTransitionPause(Transition)}.
2318 *
2319 * @param transition The transition which was resumed.
2320 */
2321 void onTransitionResume(Transition transition);
Chet Haasefaebd8f2012-05-18 14:17:57 -07002322 }
2323
2324 /**
2325 * Utility adapter class to avoid having to override all three methods
2326 * whenever someone just wants to listen for a single event.
2327 *
2328 * @hide
2329 * */
2330 public static class TransitionListenerAdapter implements TransitionListener {
2331 @Override
2332 public void onTransitionStart(Transition transition) {
2333 }
2334
2335 @Override
2336 public void onTransitionEnd(Transition transition) {
2337 }
2338
2339 @Override
2340 public void onTransitionCancel(Transition transition) {
2341 }
Chet Haase199acdf2013-07-24 18:40:55 -07002342
2343 @Override
2344 public void onTransitionPause(Transition transition) {
2345 }
2346
2347 @Override
2348 public void onTransitionResume(Transition transition) {
2349 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07002350 }
2351
Chet Haase199acdf2013-07-24 18:40:55 -07002352 /**
2353 * Holds information about each animator used when a new transition starts
2354 * while other transitions are still running to determine whether a running
2355 * animation should be canceled or a new animation noop'd. The structure holds
2356 * information about the state that an animation is going to, to be compared to
2357 * end state of a new animation.
George Mount0a778ed2013-12-13 13:35:36 -08002358 * @hide
Chet Haase199acdf2013-07-24 18:40:55 -07002359 */
George Mount0a778ed2013-12-13 13:35:36 -08002360 public static class AnimationInfo {
2361 public View view;
Chet Haase199acdf2013-07-24 18:40:55 -07002362 String name;
2363 TransitionValues values;
George Mountcf68aad2014-03-06 14:17:46 -08002364 WindowId windowId;
George Mount4c20ea22014-06-17 10:14:39 -07002365 Transition transition;
Chet Haase199acdf2013-07-24 18:40:55 -07002366
George Mount4c20ea22014-06-17 10:14:39 -07002367 AnimationInfo(View view, String name, Transition transition,
2368 WindowId windowId, TransitionValues values) {
Chet Haase199acdf2013-07-24 18:40:55 -07002369 this.view = view;
2370 this.name = name;
2371 this.values = values;
George Mountcf68aad2014-03-06 14:17:46 -08002372 this.windowId = windowId;
George Mount4c20ea22014-06-17 10:14:39 -07002373 this.transition = transition;
Chet Haase199acdf2013-07-24 18:40:55 -07002374 }
2375 }
Chet Haaseff58f922013-09-11 13:08:18 -07002376
2377 /**
2378 * Utility class for managing typed ArrayLists efficiently. In particular, this
2379 * can be useful for lists that we don't expect to be used often (eg, the exclude
2380 * lists), so we'd like to keep them nulled out by default. This causes the code to
2381 * become tedious, with constant null checks, code to allocate when necessary,
2382 * and code to null out the reference when the list is empty. This class encapsulates
2383 * all of that functionality into simple add()/remove() methods which perform the
2384 * necessary checks, allocation/null-out as appropriate, and return the
2385 * resulting list.
2386 */
2387 private static class ArrayListManager {
2388
2389 /**
2390 * Add the specified item to the list, returning the resulting list.
2391 * The returned list can either the be same list passed in or, if that
2392 * list was null, the new list that was created.
2393 *
2394 * Note that the list holds unique items; if the item already exists in the
2395 * list, the list is not modified.
2396 */
2397 static <T> ArrayList<T> add(ArrayList<T> list, T item) {
2398 if (list == null) {
2399 list = new ArrayList<T>();
2400 }
2401 if (!list.contains(item)) {
2402 list.add(item);
2403 }
2404 return list;
2405 }
2406
2407 /**
2408 * Remove the specified item from the list, returning the resulting list.
2409 * The returned list can either the be same list passed in or, if that
2410 * list becomes empty as a result of the remove(), the new list was created.
2411 */
2412 static <T> ArrayList<T> remove(ArrayList<T> list, T item) {
2413 if (list != null) {
2414 list.remove(item);
2415 if (list.isEmpty()) {
2416 list = null;
2417 }
2418 }
2419 return list;
2420 }
2421 }
2422
George Mountd6107a32014-03-10 16:51:16 -07002423 /**
2424 * Class to get the epicenter of Transition. Use
2425 * {@link #setEpicenterCallback(android.transition.Transition.EpicenterCallback)} to
2426 * set the callback used to calculate the epicenter of the Transition. Override
2427 * {@link #getEpicenter()} to return the rectangular region in screen coordinates of
2428 * the epicenter of the transition.
2429 * @see #setEpicenterCallback(android.transition.Transition.EpicenterCallback)
2430 */
2431 public static abstract class EpicenterCallback {
2432
2433 /**
2434 * Implementers must override to return the epicenter of the Transition in screen
2435 * coordinates. Transitions like {@link android.transition.Explode} depend upon
2436 * an epicenter for the Transition. In Explode, Views move toward or away from the
2437 * center of the epicenter Rect along the vector between the epicenter and the center
2438 * of the View appearing and disappearing. Some Transitions, such as
2439 * {@link android.transition.Fade} pay no attention to the epicenter.
2440 *
2441 * @param transition The transition for which the epicenter applies.
2442 * @return The Rect region of the epicenter of <code>transition</code> or null if
2443 * there is no epicenter.
2444 */
George Mountdc21d3b2014-06-05 09:42:48 -07002445 public abstract Rect onGetEpicenter(Transition transition);
George Mountd6107a32014-03-10 16:51:16 -07002446 }
Chet Haasefaebd8f2012-05-18 14:17:57 -07002447}