blob: 6458d6f9f79962738e417533defe86fe6d06ac8c [file] [log] [blame]
George Mount31a21722014-03-24 17:44:36 -07001/*
2 * Copyright (C) 2014 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 */
16package android.app;
17
George Mountc93ca162014-05-23 19:21:36 -070018import android.content.Context;
George Mountc93ca162014-05-23 19:21:36 -070019import android.graphics.Matrix;
George Mount31a21722014-03-24 17:44:36 -070020import android.graphics.Rect;
Dake Gu7bf379c2014-07-15 16:29:38 -070021import android.graphics.RectF;
George Mountc93ca162014-05-23 19:21:36 -070022import android.os.Bundle;
George Mount31a21722014-03-24 17:44:36 -070023import android.os.Handler;
George Mount480ca822014-08-08 16:35:48 -070024import android.os.Parcelable;
George Mount31a21722014-03-24 17:44:36 -070025import android.os.ResultReceiver;
26import android.transition.Transition;
George Mount31a21722014-03-24 17:44:36 -070027import android.transition.TransitionSet;
George Mount1349bb92016-03-22 13:41:57 -070028import android.transition.Visibility;
George Mount31a21722014-03-24 17:44:36 -070029import android.util.ArrayMap;
George Mountfe361d22014-07-08 17:25:25 -070030import android.view.GhostView;
George Mount31a21722014-03-24 17:44:36 -070031import android.view.View;
32import android.view.ViewGroup;
George Mountfe361d22014-07-08 17:25:25 -070033import android.view.ViewGroupOverlay;
34import android.view.ViewParent;
George Mount41725de2015-04-09 08:23:05 -070035import android.view.ViewRootImpl;
George Mountc93ca162014-05-23 19:21:36 -070036import android.view.ViewTreeObserver;
George Mount31a21722014-03-24 17:44:36 -070037import android.view.Window;
George Mountcaa03102014-04-15 09:01:32 -070038import android.widget.ImageView;
George Mount31a21722014-03-24 17:44:36 -070039
40import java.util.ArrayList;
41import java.util.Collection;
42
43/**
44 * Base class for ExitTransitionCoordinator and EnterTransitionCoordinator, classes
45 * that manage activity transitions and the communications coordinating them between
46 * Activities. The ExitTransitionCoordinator is created in the
47 * ActivityOptions#makeSceneTransitionAnimation. The EnterTransitionCoordinator
48 * is created by ActivityOptions#createEnterActivityTransition by Activity when the window is
49 * attached.
50 *
51 * Typical startActivity goes like this:
52 * 1) ExitTransitionCoordinator created with ActivityOptions#makeSceneTransitionAnimation
53 * 2) Activity#startActivity called and that calls startExit() through
54 * ActivityOptions#dispatchStartExit
55 * - Exit transition starts by setting transitioning Views to INVISIBLE
56 * 3) Launched Activity starts, creating an EnterTransitionCoordinator.
57 * - The Window is made translucent
58 * - The Window background alpha is set to 0
59 * - The transitioning views are made INVISIBLE
60 * - MSG_SET_LISTENER is sent back to the ExitTransitionCoordinator.
61 * 4) The shared element transition completes.
62 * - MSG_TAKE_SHARED_ELEMENTS is sent to the EnterTransitionCoordinator
63 * 5) The MSG_TAKE_SHARED_ELEMENTS is received by the EnterTransitionCoordinator.
64 * - Shared elements are made VISIBLE
65 * - Shared elements positions and size are set to match the end state of the calling
66 * Activity.
67 * - The shared element transition is started
68 * - If the window allows overlapping transitions, the views transition is started by setting
69 * the entering Views to VISIBLE and the background alpha is animated to opaque.
70 * - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
71 * 6) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
72 * - The shared elements are made INVISIBLE
73 * 7) The exit transition completes in the calling Activity.
74 * - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
75 * 8) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
76 * - If the window doesn't allow overlapping enter transitions, the enter transition is started
77 * by setting entering views to VISIBLE and the background is animated to opaque.
78 * 9) The background opacity animation completes.
79 * - The window is made opaque
80 * 10) The calling Activity gets an onStop() call
81 * - onActivityStopped() is called and all exited Views are made VISIBLE.
82 *
Craig Mautner73f843d2014-05-19 09:42:28 -070083 * Typical finishAfterTransition goes like this:
84 * 1) finishAfterTransition() creates an ExitTransitionCoordinator and calls startExit()
George Mount62ab9b72014-05-02 13:51:17 -070085 * - The Window start transitioning to Translucent with a new ActivityOptions.
George Mount31a21722014-03-24 17:44:36 -070086 * - If no background exists, a black background is substituted
George Mount31a21722014-03-24 17:44:36 -070087 * - The shared elements in the scene are matched against those shared elements
88 * that were sent by comparing the names.
89 * - The exit transition is started by setting Views to INVISIBLE.
George Mount62ab9b72014-05-02 13:51:17 -070090 * 2) The ActivityOptions is received by the Activity and an EnterTransitionCoordinator is created.
George Mount31a21722014-03-24 17:44:36 -070091 * - All transitioning views are made VISIBLE to reverse what was done when onActivityStopped()
92 * was called
93 * 3) The Window is made translucent and a callback is received
94 * - The background alpha is animated to 0
95 * 4) The background alpha animation completes
96 * 5) The shared element transition completes
97 * - After both 4 & 5 complete, MSG_TAKE_SHARED_ELEMENTS is sent to the
George Mount62ab9b72014-05-02 13:51:17 -070098 * EnterTransitionCoordinator
99 * 6) MSG_TAKE_SHARED_ELEMENTS is received by EnterTransitionCoordinator
George Mount31a21722014-03-24 17:44:36 -0700100 * - Shared elements are made VISIBLE
101 * - Shared elements positions and size are set to match the end state of the calling
102 * Activity.
103 * - The shared element transition is started
104 * - If the window allows overlapping transitions, the views transition is started by setting
105 * the entering Views to VISIBLE.
George Mount62ab9b72014-05-02 13:51:17 -0700106 * - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
107 * 7) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
George Mount31a21722014-03-24 17:44:36 -0700108 * - The shared elements are made INVISIBLE
109 * 8) The exit transition completes in the finishing Activity.
George Mount62ab9b72014-05-02 13:51:17 -0700110 * - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
George Mount31a21722014-03-24 17:44:36 -0700111 * - finish() is called on the exiting Activity
George Mount62ab9b72014-05-02 13:51:17 -0700112 * 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
George Mount31a21722014-03-24 17:44:36 -0700113 * - If the window doesn't allow overlapping enter transitions, the enter transition is started
114 * by setting entering views to VISIBLE.
115 */
116abstract class ActivityTransitionCoordinator extends ResultReceiver {
117 private static final String TAG = "ActivityTransitionCoordinator";
118
119 /**
George Mount31a21722014-03-24 17:44:36 -0700120 * For Activity transitions, the called Activity's listener to receive calls
121 * when transitions complete.
122 */
George Mount62ab9b72014-05-02 13:51:17 -0700123 static final String KEY_REMOTE_RECEIVER = "android:remoteReceiver";
George Mount31a21722014-03-24 17:44:36 -0700124
Dake Gu7bf379c2014-07-15 16:29:38 -0700125 protected static final String KEY_SCREEN_LEFT = "shared_element:screenLeft";
126 protected static final String KEY_SCREEN_TOP = "shared_element:screenTop";
127 protected static final String KEY_SCREEN_RIGHT = "shared_element:screenRight";
128 protected static final String KEY_SCREEN_BOTTOM= "shared_element:screenBottom";
George Mount62ab9b72014-05-02 13:51:17 -0700129 protected static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
George Mount480ca822014-08-08 16:35:48 -0700130 protected static final String KEY_SNAPSHOT = "shared_element:bitmap";
George Mount62ab9b72014-05-02 13:51:17 -0700131 protected static final String KEY_SCALE_TYPE = "shared_element:scaleType";
132 protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
George Mount26c82b62014-08-08 15:43:59 -0700133 protected static final String KEY_ELEVATION = "shared_element:elevation";
George Mount080443b2014-05-05 10:47:00 -0700134
George Mount62ab9b72014-05-02 13:51:17 -0700135 protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
George Mount31a21722014-03-24 17:44:36 -0700136
137 /**
138 * Sent by the exiting coordinator (either EnterTransitionCoordinator
139 * or ExitTransitionCoordinator) after the shared elements have
140 * become stationary (shared element transition completes). This tells
141 * the remote coordinator to take control of the shared elements and
142 * that animations may begin. The remote Activity won't start entering
143 * until this message is received, but may wait for
144 * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
145 */
George Mount62ab9b72014-05-02 13:51:17 -0700146 public static final int MSG_SET_REMOTE_RECEIVER = 100;
George Mount31a21722014-03-24 17:44:36 -0700147
148 /**
149 * Sent by the entering coordinator to tell the exiting coordinator
150 * to hide its shared elements after it has started its shared
151 * element transition. This is temporary until the
152 * interlock of shared elements is figured out.
153 */
154 public static final int MSG_HIDE_SHARED_ELEMENTS = 101;
155
156 /**
George Mount31a21722014-03-24 17:44:36 -0700157 * Sent by the exiting coordinator (either EnterTransitionCoordinator
158 * or ExitTransitionCoordinator) after the shared elements have
159 * become stationary (shared element transition completes). This tells
160 * the remote coordinator to take control of the shared elements and
161 * that animations may begin. The remote Activity won't start entering
162 * until this message is received, but may wait for
163 * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
164 */
George Mount62ab9b72014-05-02 13:51:17 -0700165 public static final int MSG_TAKE_SHARED_ELEMENTS = 103;
George Mount31a21722014-03-24 17:44:36 -0700166
167 /**
168 * Sent by the exiting coordinator (either
169 * EnterTransitionCoordinator or ExitTransitionCoordinator) after
170 * the exiting Views have finished leaving the scene. This will
171 * be ignored if allowOverlappingTransitions() is true on the
172 * remote coordinator. If it is false, it will trigger the enter
173 * transition to start.
174 */
George Mount62ab9b72014-05-02 13:51:17 -0700175 public static final int MSG_EXIT_TRANSITION_COMPLETE = 104;
George Mount31a21722014-03-24 17:44:36 -0700176
177 /**
178 * Sent by Activity#startActivity to begin the exit transition.
179 */
George Mount62ab9b72014-05-02 13:51:17 -0700180 public static final int MSG_START_EXIT_TRANSITION = 105;
George Mount31a21722014-03-24 17:44:36 -0700181
George Mount62ab9b72014-05-02 13:51:17 -0700182 /**
183 * It took too long for a message from the entering Activity, so we canceled the transition.
184 */
185 public static final int MSG_CANCEL = 106;
George Mount31a21722014-03-24 17:44:36 -0700186
George Mountc93ca162014-05-23 19:21:36 -0700187 /**
188 * When returning, this is the destination location for the shared element.
189 */
190 public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
191
George Mounta0a02602014-06-20 18:22:26 -0700192 private Window mWindow;
George Mount62ab9b72014-05-02 13:51:17 -0700193 final protected ArrayList<String> mAllSharedElementNames;
194 final protected ArrayList<View> mSharedElements = new ArrayList<View>();
195 final protected ArrayList<String> mSharedElementNames = new ArrayList<String>();
George Mountb694e082014-09-12 07:34:52 -0700196 protected ArrayList<View> mTransitioningViews = new ArrayList<View>();
George Mount65580562014-08-29 08:15:48 -0700197 protected SharedElementCallback mListener;
George Mount62ab9b72014-05-02 13:51:17 -0700198 protected ResultReceiver mResultReceiver;
199 final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
George Mountc9b6df82014-05-21 15:07:04 -0700200 final protected boolean mIsReturning;
George Mount67d92432014-06-06 13:34:20 -0700201 private Runnable mPendingTransition;
202 private boolean mIsStartingTransition;
George Mountfe361d22014-07-08 17:25:25 -0700203 private ArrayList<GhostViewListeners> mGhostViewListeners =
204 new ArrayList<GhostViewListeners>();
George Mount0f0c4732014-09-05 13:47:47 -0700205 private ArrayMap<View, Float> mOriginalAlphas = new ArrayMap<View, Float>();
George Mount333b8092014-10-21 15:09:11 -0700206 private ArrayList<Matrix> mSharedElementParentMatrices;
George Mount41725de2015-04-09 08:23:05 -0700207 private boolean mSharedElementTransitionComplete;
208 private boolean mViewsTransitionComplete;
George Mount67d92432014-06-06 13:34:20 -0700209
George Mount8c2614c2014-06-10 11:12:01 -0700210 public ActivityTransitionCoordinator(Window window,
211 ArrayList<String> allSharedElementNames,
George Mount65580562014-08-29 08:15:48 -0700212 SharedElementCallback listener, boolean isReturning) {
George Mount31a21722014-03-24 17:44:36 -0700213 super(new Handler());
214 mWindow = window;
George Mount62ab9b72014-05-02 13:51:17 -0700215 mListener = listener;
216 mAllSharedElementNames = allSharedElementNames;
George Mountc9b6df82014-05-21 15:07:04 -0700217 mIsReturning = isReturning;
George Mount8c2614c2014-06-10 11:12:01 -0700218 }
219
George Mount1fecfb22014-06-18 14:55:55 -0700220 protected void viewsReady(ArrayMap<String, View> sharedElements) {
George Mountce725a42014-08-27 16:10:46 -0700221 sharedElements.retainAll(mAllSharedElementNames);
George Mount1732f522014-09-17 16:59:36 -0700222 if (mListener != null) {
223 mListener.onMapSharedElements(mAllSharedElementNames, sharedElements);
224 }
George Mount333b8092014-10-21 15:09:11 -0700225 setSharedElements(sharedElements);
George Mountb694e082014-09-12 07:34:52 -0700226 if (getViewsTransition() != null && mTransitioningViews != null) {
George Mount48bd13c2014-09-12 10:54:54 -0700227 ViewGroup decorView = getDecor();
228 if (decorView != null) {
229 decorView.captureTransitioningViews(mTransitioningViews);
230 }
George Mount62ab9b72014-05-02 13:51:17 -0700231 mTransitioningViews.removeAll(mSharedElements);
George Mount31a21722014-03-24 17:44:36 -0700232 }
George Mount62ab9b72014-05-02 13:51:17 -0700233 setEpicenter();
George Mount31a21722014-03-24 17:44:36 -0700234 }
235
George Mount333b8092014-10-21 15:09:11 -0700236 /**
237 * Iterates over the shared elements and adds them to the members in order.
238 * Shared elements that are nested in other shared elements are placed after the
239 * elements that they are nested in. This means that layout ordering can be done
240 * from first to last.
241 *
242 * @param sharedElements The map of transition names to shared elements to set into
243 * the member fields.
244 */
245 private void setSharedElements(ArrayMap<String, View> sharedElements) {
246 boolean isFirstRun = true;
247 while (!sharedElements.isEmpty()) {
248 final int numSharedElements = sharedElements.size();
249 for (int i = numSharedElements - 1; i >= 0; i--) {
250 final View view = sharedElements.valueAt(i);
251 final String name = sharedElements.keyAt(i);
252 if (isFirstRun && (view == null || !view.isAttachedToWindow() || name == null)) {
253 sharedElements.removeAt(i);
George Mount42161c52015-02-24 16:18:09 -0800254 } else if (!isNested(view, sharedElements)) {
255 mSharedElementNames.add(name);
256 mSharedElements.add(view);
257 sharedElements.removeAt(i);
George Mount333b8092014-10-21 15:09:11 -0700258 }
259 }
260 isFirstRun = false;
261 }
262 }
263
264 /**
265 * Returns true when view is nested in any of the values of sharedElements.
266 */
267 private static boolean isNested(View view, ArrayMap<String, View> sharedElements) {
268 ViewParent parent = view.getParent();
269 boolean isNested = false;
270 while (parent instanceof View) {
271 View parentView = (View) parent;
272 if (sharedElements.containsValue(parentView)) {
273 isNested = true;
274 break;
275 }
276 parent = parentView.getParent();
277 }
278 return isNested;
279 }
280
George Mount60625b02014-06-24 07:46:23 -0700281 protected void stripOffscreenViews() {
George Mountb694e082014-09-12 07:34:52 -0700282 if (mTransitioningViews == null) {
283 return;
284 }
George Mount60625b02014-06-24 07:46:23 -0700285 Rect r = new Rect();
286 for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
287 View view = mTransitioningViews.get(i);
288 if (!view.getGlobalVisibleRect(r)) {
289 mTransitioningViews.remove(i);
George Mount653ea662014-09-12 13:56:32 -0700290 showView(view, true);
George Mount60625b02014-06-24 07:46:23 -0700291 }
292 }
293 }
294
George Mount31a21722014-03-24 17:44:36 -0700295 protected Window getWindow() {
296 return mWindow;
297 }
298
George Mounta2bbbb32014-08-12 10:16:20 -0700299 public ViewGroup getDecor() {
George Mount31a21722014-03-24 17:44:36 -0700300 return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView();
301 }
302
George Mount62ab9b72014-05-02 13:51:17 -0700303 /**
304 * Sets the transition epicenter to the position of the first shared element.
305 */
306 protected void setEpicenter() {
307 View epicenter = null;
George Mountc6186bf2014-09-04 16:33:59 -0700308 if (!mAllSharedElementNames.isEmpty() && !mSharedElementNames.isEmpty()) {
309 int index = mSharedElementNames.indexOf(mAllSharedElementNames.get(0));
310 if (index >= 0) {
311 epicenter = mSharedElements.get(index);
312 }
George Mount31a21722014-03-24 17:44:36 -0700313 }
George Mount62ab9b72014-05-02 13:51:17 -0700314 setEpicenter(epicenter);
George Mount31a21722014-03-24 17:44:36 -0700315 }
316
George Mount62ab9b72014-05-02 13:51:17 -0700317 private void setEpicenter(View view) {
318 if (view == null) {
319 mEpicenterCallback.setEpicenter(null);
George Mount31a21722014-03-24 17:44:36 -0700320 } else {
George Mount8e43d6d2014-06-05 17:25:46 -0700321 Rect epicenter = new Rect();
322 view.getBoundsOnScreen(epicenter);
George Mount62ab9b72014-05-02 13:51:17 -0700323 mEpicenterCallback.setEpicenter(epicenter);
George Mount31a21722014-03-24 17:44:36 -0700324 }
325 }
326
George Mount62ab9b72014-05-02 13:51:17 -0700327 public ArrayList<String> getAcceptedNames() {
328 return mSharedElementNames;
George Mount31a21722014-03-24 17:44:36 -0700329 }
330
George Mount62ab9b72014-05-02 13:51:17 -0700331 public ArrayList<String> getMappedNames() {
332 ArrayList<String> names = new ArrayList<String>(mSharedElements.size());
333 for (int i = 0; i < mSharedElements.size(); i++) {
George Mount0a2ae002014-06-23 14:57:27 +0000334 names.add(mSharedElements.get(i).getTransitionName());
George Mount31a21722014-03-24 17:44:36 -0700335 }
George Mount62ab9b72014-05-02 13:51:17 -0700336 return names;
George Mount31a21722014-03-24 17:44:36 -0700337 }
338
George Mount700db2a2014-07-07 17:17:49 -0700339 public ArrayList<View> copyMappedViews() {
340 return new ArrayList<View>(mSharedElements);
George Mount1fecfb22014-06-18 14:55:55 -0700341 }
342
George Mount8c2614c2014-06-10 11:12:01 -0700343 public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
344
George Mount88815022014-06-25 14:33:54 -0700345 protected Transition setTargets(Transition transition, boolean add) {
346 if (transition == null || (add &&
347 (mTransitioningViews == null || mTransitioningViews.isEmpty()))) {
George Mount31a21722014-03-24 17:44:36 -0700348 return null;
349 }
George Mountd4c3c912014-06-09 12:31:34 -0700350 // Add the targets to a set containing transition so that transition
351 // remains unaffected. We don't want to modify the targets of transition itself.
George Mount31a21722014-03-24 17:44:36 -0700352 TransitionSet set = new TransitionSet();
George Mount88815022014-06-25 14:33:54 -0700353 if (mTransitioningViews != null) {
354 for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
355 View view = mTransitioningViews.get(i);
356 if (add) {
357 set.addTarget(view);
358 } else {
359 set.excludeTarget(view, true);
360 }
George Mount31a21722014-03-24 17:44:36 -0700361 }
362 }
George Mountd4c3c912014-06-09 12:31:34 -0700363 // By adding the transition after addTarget, we prevent addTarget from
364 // affecting transition.
365 set.addTransition(transition);
George Mountfe361d22014-07-08 17:25:25 -0700366
367 if (!add && mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
368 // Allow children of excluded transitioning views, but not the views themselves
369 set = new TransitionSet().addTransition(set);
370 }
371
George Mount31a21722014-03-24 17:44:36 -0700372 return set;
373 }
374
George Mount88815022014-06-25 14:33:54 -0700375 protected Transition configureTransition(Transition transition,
376 boolean includeTransitioningViews) {
George Mount31a21722014-03-24 17:44:36 -0700377 if (transition != null) {
George Mount62ab9b72014-05-02 13:51:17 -0700378 transition = transition.clone();
379 transition.setEpicenterCallback(mEpicenterCallback);
George Mount88815022014-06-25 14:33:54 -0700380 transition = setTargets(transition, includeTransitioningViews);
George Mount31a21722014-03-24 17:44:36 -0700381 }
George Mount1349bb92016-03-22 13:41:57 -0700382 noLayoutSuppressionForVisibilityTransitions(transition);
George Mount31a21722014-03-24 17:44:36 -0700383 return transition;
384 }
385
George Mount62ab9b72014-05-02 13:51:17 -0700386 protected static Transition mergeTransitions(Transition transition1, Transition transition2) {
387 if (transition1 == null) {
388 return transition2;
389 } else if (transition2 == null) {
390 return transition1;
391 } else {
392 TransitionSet transitionSet = new TransitionSet();
393 transitionSet.addTransition(transition1);
394 transitionSet.addTransition(transition2);
395 return transitionSet;
George Mountcaa03102014-04-15 09:01:32 -0700396 }
George Mountcaa03102014-04-15 09:01:32 -0700397 }
398
George Mount1fecfb22014-06-18 14:55:55 -0700399 protected ArrayMap<String, View> mapSharedElements(ArrayList<String> accepted,
400 ArrayList<View> localViews) {
401 ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
George Mountce725a42014-08-27 16:10:46 -0700402 if (accepted != null) {
403 for (int i = 0; i < accepted.size(); i++) {
404 sharedElements.put(accepted.get(i), localViews.get(i));
George Mount62ab9b72014-05-02 13:51:17 -0700405 }
George Mountce725a42014-08-27 16:10:46 -0700406 } else {
George Mount48bd13c2014-09-12 10:54:54 -0700407 ViewGroup decorView = getDecor();
408 if (decorView != null) {
409 decorView.findNamedViews(sharedElements);
410 }
George Mount1fecfb22014-06-18 14:55:55 -0700411 }
412 return sharedElements;
413 }
414
George Mount62ab9b72014-05-02 13:51:17 -0700415 protected void setResultReceiver(ResultReceiver resultReceiver) {
416 mResultReceiver = resultReceiver;
George Mount080443b2014-05-05 10:47:00 -0700417 }
418
George Mounta712e8c2014-05-20 15:10:20 -0700419 protected abstract Transition getViewsTransition();
George Mount080443b2014-05-05 10:47:00 -0700420
George Mountfe361d22014-07-08 17:25:25 -0700421 private void setSharedElementState(View view, String name, Bundle transitionArgs,
Dake Gu7bf379c2014-07-15 16:29:38 -0700422 Matrix tempMatrix, RectF tempRect, int[] decorLoc) {
George Mountc93ca162014-05-23 19:21:36 -0700423 Bundle sharedElementBundle = transitionArgs.getBundle(name);
424 if (sharedElementBundle == null) {
425 return;
426 }
427
428 if (view instanceof ImageView) {
429 int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
430 if (scaleTypeInt >= 0) {
431 ImageView imageView = (ImageView) view;
432 ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
433 imageView.setScaleType(scaleType);
434 if (scaleType == ImageView.ScaleType.MATRIX) {
435 float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
Dake Gu7bf379c2014-07-15 16:29:38 -0700436 tempMatrix.setValues(matrixValues);
437 imageView.setImageMatrix(tempMatrix);
George Mountc93ca162014-05-23 19:21:36 -0700438 }
439 }
440 }
441
442 float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
443 view.setTranslationZ(z);
George Mount26c82b62014-08-08 15:43:59 -0700444 float elevation = sharedElementBundle.getFloat(KEY_ELEVATION);
445 view.setElevation(elevation);
George Mountc93ca162014-05-23 19:21:36 -0700446
Dake Gu7bf379c2014-07-15 16:29:38 -0700447 float left = sharedElementBundle.getFloat(KEY_SCREEN_LEFT);
448 float top = sharedElementBundle.getFloat(KEY_SCREEN_TOP);
449 float right = sharedElementBundle.getFloat(KEY_SCREEN_RIGHT);
450 float bottom = sharedElementBundle.getFloat(KEY_SCREEN_BOTTOM);
George Mountc93ca162014-05-23 19:21:36 -0700451
Dake Gu7bf379c2014-07-15 16:29:38 -0700452 if (decorLoc != null) {
453 left -= decorLoc[0];
454 top -= decorLoc[1];
George Mount480ca822014-08-08 16:35:48 -0700455 right -= decorLoc[0];
456 bottom -= decorLoc[1];
Dake Gu7bf379c2014-07-15 16:29:38 -0700457 } else {
458 // Find the location in the view's parent
George Mountfe361d22014-07-08 17:25:25 -0700459 getSharedElementParentMatrix(view, tempMatrix);
Dake Gu7bf379c2014-07-15 16:29:38 -0700460 tempRect.set(left, top, right, bottom);
461 tempMatrix.mapRect(tempRect);
462
463 float leftInParent = tempRect.left;
464 float topInParent = tempRect.top;
465
466 // Find the size of the view
467 view.getInverseMatrix().mapRect(tempRect);
468 float width = tempRect.width();
469 float height = tempRect.height();
470
471 // Now determine the offset due to view transform:
472 view.setLeft(0);
473 view.setTop(0);
474 view.setRight(Math.round(width));
475 view.setBottom(Math.round(height));
476 tempRect.set(0, 0, width, height);
477 view.getMatrix().mapRect(tempRect);
478
Doris Liu18c2b0e2015-05-01 11:02:01 -0700479 left = leftInParent - tempRect.left;
480 top = topInParent - tempRect.top;
Dake Gu7bf379c2014-07-15 16:29:38 -0700481 right = left + width;
482 bottom = top + height;
483 }
484
485 int x = Math.round(left);
486 int y = Math.round(top);
487 int width = Math.round(right) - x;
488 int height = Math.round(bottom) - y;
George Mountc93ca162014-05-23 19:21:36 -0700489 int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
490 int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
491 view.measure(widthSpec, heightSpec);
492
Dake Gu7bf379c2014-07-15 16:29:38 -0700493 view.layout(x, y, x + width, y + height);
George Mountc93ca162014-05-23 19:21:36 -0700494 }
495
George Mount333b8092014-10-21 15:09:11 -0700496 private void setSharedElementMatrices() {
497 int numSharedElements = mSharedElements.size();
498 if (numSharedElements > 0) {
499 mSharedElementParentMatrices = new ArrayList<Matrix>(numSharedElements);
500 }
501 for (int i = 0; i < numSharedElements; i++) {
502 View view = mSharedElements.get(i);
503
504 // Find the location in the view's parent
505 ViewGroup parent = (ViewGroup) view.getParent();
506 Matrix matrix = new Matrix();
507 parent.transformMatrixToLocal(matrix);
Doris Liu18c2b0e2015-05-01 11:02:01 -0700508 matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
George Mount333b8092014-10-21 15:09:11 -0700509 mSharedElementParentMatrices.add(matrix);
510 }
511 }
512
513 private void getSharedElementParentMatrix(View view, Matrix matrix) {
George Mount42161c52015-02-24 16:18:09 -0800514 final int index = mSharedElementParentMatrices == null ? -1
515 : mSharedElements.indexOf(view);
516 if (index < 0) {
George Mount333b8092014-10-21 15:09:11 -0700517 matrix.reset();
518 ViewParent viewParent = view.getParent();
519 if (viewParent instanceof ViewGroup) {
520 // Find the location in the view's parent
521 ViewGroup parent = (ViewGroup) viewParent;
522 parent.transformMatrixToLocal(matrix);
Doris Liu18c2b0e2015-05-01 11:02:01 -0700523 matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
George Mount333b8092014-10-21 15:09:11 -0700524 }
George Mount42161c52015-02-24 16:18:09 -0800525 } else {
526 // The indices of mSharedElementParentMatrices matches the
527 // mSharedElement matrices.
528 Matrix parentMatrix = mSharedElementParentMatrices.get(index);
529 matrix.set(parentMatrix);
George Mount333b8092014-10-21 15:09:11 -0700530 }
George Mountfe361d22014-07-08 17:25:25 -0700531 }
532
Dake Guc18f4cc2014-07-11 17:48:37 -0700533 protected ArrayList<SharedElementOriginalState> setSharedElementState(
George Mountc93ca162014-05-23 19:21:36 -0700534 Bundle sharedElementState, final ArrayList<View> snapshots) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700535 ArrayList<SharedElementOriginalState> originalImageState =
536 new ArrayList<SharedElementOriginalState>();
George Mountc93ca162014-05-23 19:21:36 -0700537 if (sharedElementState != null) {
Dake Gu7bf379c2014-07-15 16:29:38 -0700538 Matrix tempMatrix = new Matrix();
539 RectF tempRect = new RectF();
George Mount1732f522014-09-17 16:59:36 -0700540 final int numSharedElements = mSharedElements.size();
541 for (int i = 0; i < numSharedElements; i++) {
George Mountc93ca162014-05-23 19:21:36 -0700542 View sharedElement = mSharedElements.get(i);
543 String name = mSharedElementNames.get(i);
Dake Guc18f4cc2014-07-11 17:48:37 -0700544 SharedElementOriginalState originalState = getOldSharedElementState(sharedElement,
George Mountc93ca162014-05-23 19:21:36 -0700545 name, sharedElementState);
Dake Guc18f4cc2014-07-11 17:48:37 -0700546 originalImageState.add(originalState);
Dake Gu7bf379c2014-07-15 16:29:38 -0700547 setSharedElementState(sharedElement, name, sharedElementState,
548 tempMatrix, tempRect, null);
George Mountc93ca162014-05-23 19:21:36 -0700549 }
550 }
George Mount1732f522014-09-17 16:59:36 -0700551 if (mListener != null) {
552 mListener.onSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
553 }
George Mountfe361d22014-07-08 17:25:25 -0700554 return originalImageState;
555 }
George Mountc93ca162014-05-23 19:21:36 -0700556
George Mountfe361d22014-07-08 17:25:25 -0700557 protected void notifySharedElementEnd(ArrayList<View> snapshots) {
George Mount1732f522014-09-17 16:59:36 -0700558 if (mListener != null) {
559 mListener.onSharedElementEnd(mSharedElementNames, mSharedElements, snapshots);
560 }
George Mountfe361d22014-07-08 17:25:25 -0700561 }
562
563 protected void scheduleSetSharedElementEnd(final ArrayList<View> snapshots) {
George Mount6e7fb602014-09-04 16:20:20 -0700564 final View decorView = getDecor();
George Mount48bd13c2014-09-12 10:54:54 -0700565 if (decorView != null) {
566 decorView.getViewTreeObserver().addOnPreDrawListener(
567 new ViewTreeObserver.OnPreDrawListener() {
568 @Override
569 public boolean onPreDraw() {
570 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
571 notifySharedElementEnd(snapshots);
572 return true;
573 }
George Mountc93ca162014-05-23 19:21:36 -0700574 }
George Mount48bd13c2014-09-12 10:54:54 -0700575 );
576 }
George Mountc93ca162014-05-23 19:21:36 -0700577 }
578
Dake Guc18f4cc2014-07-11 17:48:37 -0700579 private static SharedElementOriginalState getOldSharedElementState(View view, String name,
George Mountc93ca162014-05-23 19:21:36 -0700580 Bundle transitionArgs) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700581
582 SharedElementOriginalState state = new SharedElementOriginalState();
583 state.mLeft = view.getLeft();
584 state.mTop = view.getTop();
585 state.mRight = view.getRight();
586 state.mBottom = view.getBottom();
587 state.mMeasuredWidth = view.getMeasuredWidth();
588 state.mMeasuredHeight = view.getMeasuredHeight();
George Mount26c82b62014-08-08 15:43:59 -0700589 state.mTranslationZ = view.getTranslationZ();
590 state.mElevation = view.getElevation();
George Mountc93ca162014-05-23 19:21:36 -0700591 if (!(view instanceof ImageView)) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700592 return state;
George Mountc93ca162014-05-23 19:21:36 -0700593 }
594 Bundle bundle = transitionArgs.getBundle(name);
595 if (bundle == null) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700596 return state;
George Mountc93ca162014-05-23 19:21:36 -0700597 }
598 int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
599 if (scaleTypeInt < 0) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700600 return state;
George Mountc93ca162014-05-23 19:21:36 -0700601 }
602
603 ImageView imageView = (ImageView) view;
Dake Guc18f4cc2014-07-11 17:48:37 -0700604 state.mScaleType = imageView.getScaleType();
605 if (state.mScaleType == ImageView.ScaleType.MATRIX) {
606 state.mMatrix = new Matrix(imageView.getImageMatrix());
George Mountc93ca162014-05-23 19:21:36 -0700607 }
Dake Guc18f4cc2014-07-11 17:48:37 -0700608 return state;
George Mountc93ca162014-05-23 19:21:36 -0700609 }
610
611 protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
612 int numSharedElements = names.size();
George Mountc93ca162014-05-23 19:21:36 -0700613 ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
George Mount7c479f52014-10-15 16:21:51 -0700614 if (numSharedElements == 0) {
615 return snapshots;
616 }
George Mountc93ca162014-05-23 19:21:36 -0700617 Context context = getWindow().getContext();
Dake Gu7bf379c2014-07-15 16:29:38 -0700618 int[] decorLoc = new int[2];
George Mount48bd13c2014-09-12 10:54:54 -0700619 ViewGroup decorView = getDecor();
620 if (decorView != null) {
621 decorView.getLocationOnScreen(decorLoc);
622 }
George Mountca5094a2014-10-30 11:28:00 -0700623 Matrix tempMatrix = new Matrix();
George Mountc93ca162014-05-23 19:21:36 -0700624 for (String name: names) {
625 Bundle sharedElementBundle = state.getBundle(name);
George Mount92692c02014-12-01 16:44:05 -0800626 View snapshot = null;
George Mountc93ca162014-05-23 19:21:36 -0700627 if (sharedElementBundle != null) {
George Mount480ca822014-08-08 16:35:48 -0700628 Parcelable parcelable = sharedElementBundle.getParcelable(KEY_SNAPSHOT);
George Mount1732f522014-09-17 16:59:36 -0700629 if (parcelable != null && mListener != null) {
George Mount65580562014-08-29 08:15:48 -0700630 snapshot = mListener.onCreateSnapshotView(context, parcelable);
George Mountc93ca162014-05-23 19:21:36 -0700631 }
George Mount480ca822014-08-08 16:35:48 -0700632 if (snapshot != null) {
George Mountca5094a2014-10-30 11:28:00 -0700633 setSharedElementState(snapshot, name, state, tempMatrix, null, decorLoc);
George Mount480ca822014-08-08 16:35:48 -0700634 }
George Mountc93ca162014-05-23 19:21:36 -0700635 }
George Mount92692c02014-12-01 16:44:05 -0800636 // Even null snapshots are added so they remain in the same order as shared elements.
637 snapshots.add(snapshot);
George Mountc93ca162014-05-23 19:21:36 -0700638 }
639 return snapshots;
640 }
641
Dake Guc18f4cc2014-07-11 17:48:37 -0700642 protected static void setOriginalSharedElementState(ArrayList<View> sharedElements,
643 ArrayList<SharedElementOriginalState> originalState) {
George Mountc93ca162014-05-23 19:21:36 -0700644 for (int i = 0; i < originalState.size(); i++) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700645 View view = sharedElements.get(i);
646 SharedElementOriginalState state = originalState.get(i);
647 if (view instanceof ImageView && state.mScaleType != null) {
648 ImageView imageView = (ImageView) view;
649 imageView.setScaleType(state.mScaleType);
650 if (state.mScaleType == ImageView.ScaleType.MATRIX) {
651 imageView.setImageMatrix(state.mMatrix);
652 }
653 }
George Mount26c82b62014-08-08 15:43:59 -0700654 view.setElevation(state.mElevation);
655 view.setTranslationZ(state.mTranslationZ);
656 int widthSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredWidth,
Dake Guc18f4cc2014-07-11 17:48:37 -0700657 View.MeasureSpec.EXACTLY);
658 int heightSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredHeight,
659 View.MeasureSpec.EXACTLY);
660 view.measure(widthSpec, heightSpec);
661 view.layout(state.mLeft, state.mTop, state.mRight, state.mBottom);
George Mountc93ca162014-05-23 19:21:36 -0700662 }
663 }
664
665 protected Bundle captureSharedElementState() {
666 Bundle bundle = new Bundle();
Dake Gu7bf379c2014-07-15 16:29:38 -0700667 RectF tempBounds = new RectF();
668 Matrix tempMatrix = new Matrix();
Dake Guf03c1392014-07-25 15:36:23 -0700669 for (int i = 0; i < mSharedElements.size(); i++) {
George Mountc93ca162014-05-23 19:21:36 -0700670 View sharedElement = mSharedElements.get(i);
671 String name = mSharedElementNames.get(i);
Dake Gu7bf379c2014-07-15 16:29:38 -0700672 captureSharedElementState(sharedElement, name, bundle, tempMatrix, tempBounds);
George Mountc93ca162014-05-23 19:21:36 -0700673 }
674 return bundle;
675 }
676
George Mounta0a02602014-06-20 18:22:26 -0700677 protected void clearState() {
678 // Clear the state so that we can't hold any references accidentally and leak memory.
679 mWindow = null;
George Mounta0a02602014-06-20 18:22:26 -0700680 mSharedElements.clear();
George Mountb694e082014-09-12 07:34:52 -0700681 mTransitioningViews = null;
George Mount0f0c4732014-09-05 13:47:47 -0700682 mOriginalAlphas.clear();
George Mounta0a02602014-06-20 18:22:26 -0700683 mResultReceiver = null;
684 mPendingTransition = null;
Dake Gufc0fc0e2014-08-01 15:39:11 -0700685 mListener = null;
George Mount333b8092014-10-21 15:09:11 -0700686 mSharedElementParentMatrices = null;
George Mounta0a02602014-06-20 18:22:26 -0700687 }
688
George Mounted1e01d2014-06-05 13:49:12 -0700689 protected long getFadeDuration() {
690 return getWindow().getTransitionBackgroundFadeDuration();
691 }
692
George Mount0f0c4732014-09-05 13:47:47 -0700693 protected void hideViews(ArrayList<View> views) {
George Mountfe361d22014-07-08 17:25:25 -0700694 int count = views.size();
695 for (int i = 0; i < count; i++) {
George Mount0f0c4732014-09-05 13:47:47 -0700696 View view = views.get(i);
697 if (!mOriginalAlphas.containsKey(view)) {
698 mOriginalAlphas.put(view, view.getAlpha());
699 }
700 view.setAlpha(0f);
George Mount0f0c4732014-09-05 13:47:47 -0700701 }
702 }
703
George Mountce2ee3d2014-09-09 15:04:31 -0700704 protected void showViews(ArrayList<View> views, boolean setTransitionAlpha) {
George Mount0f0c4732014-09-05 13:47:47 -0700705 int count = views.size();
706 for (int i = 0; i < count; i++) {
George Mount653ea662014-09-12 13:56:32 -0700707 showView(views.get(i), setTransitionAlpha);
708 }
709 }
710
711 private void showView(View view, boolean setTransitionAlpha) {
712 Float alpha = mOriginalAlphas.remove(view);
713 if (alpha != null) {
714 view.setAlpha(alpha);
715 }
716 if (setTransitionAlpha) {
717 view.setTransitionAlpha(1f);
George Mountb5ef7f82014-07-09 14:55:03 -0700718 }
719 }
720
George Mountc93ca162014-05-23 19:21:36 -0700721 /**
722 * Captures placement information for Views with a shared element name for
723 * Activity Transitions.
724 *
725 * @param view The View to capture the placement information for.
726 * @param name The shared element name in the target Activity to apply the placement
727 * information for.
728 * @param transitionArgs Bundle to store shared element placement information.
George Mount8e43d6d2014-06-05 17:25:46 -0700729 * @param tempBounds A temporary Rect for capturing the current location of views.
George Mountc93ca162014-05-23 19:21:36 -0700730 */
George Mount480ca822014-08-08 16:35:48 -0700731 protected void captureSharedElementState(View view, String name, Bundle transitionArgs,
Dake Gu7bf379c2014-07-15 16:29:38 -0700732 Matrix tempMatrix, RectF tempBounds) {
George Mountc93ca162014-05-23 19:21:36 -0700733 Bundle sharedElementBundle = new Bundle();
Dake Gu7bf379c2014-07-15 16:29:38 -0700734 tempMatrix.reset();
735 view.transformMatrixToGlobal(tempMatrix);
George Mount8e43d6d2014-06-05 17:25:46 -0700736 tempBounds.set(0, 0, view.getWidth(), view.getHeight());
Dake Gu7bf379c2014-07-15 16:29:38 -0700737 tempMatrix.mapRect(tempBounds);
George Mountc93ca162014-05-23 19:21:36 -0700738
Dake Gu7bf379c2014-07-15 16:29:38 -0700739 sharedElementBundle.putFloat(KEY_SCREEN_LEFT, tempBounds.left);
740 sharedElementBundle.putFloat(KEY_SCREEN_RIGHT, tempBounds.right);
741 sharedElementBundle.putFloat(KEY_SCREEN_TOP, tempBounds.top);
742 sharedElementBundle.putFloat(KEY_SCREEN_BOTTOM, tempBounds.bottom);
George Mountc93ca162014-05-23 19:21:36 -0700743 sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
George Mount26c82b62014-08-08 15:43:59 -0700744 sharedElementBundle.putFloat(KEY_ELEVATION, view.getElevation());
George Mountc93ca162014-05-23 19:21:36 -0700745
George Mount1732f522014-09-17 16:59:36 -0700746 Parcelable bitmap = null;
747 if (mListener != null) {
748 bitmap = mListener.onCaptureSharedElementSnapshot(view, tempMatrix, tempBounds);
749 }
750
George Mount480ca822014-08-08 16:35:48 -0700751 if (bitmap != null) {
752 sharedElementBundle.putParcelable(KEY_SNAPSHOT, bitmap);
George Mountc93ca162014-05-23 19:21:36 -0700753 }
754
755 if (view instanceof ImageView) {
756 ImageView imageView = (ImageView) view;
757 int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
758 sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
759 if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
760 float[] matrix = new float[9];
761 imageView.getImageMatrix().getValues(matrix);
762 sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
763 }
764 }
765
766 transitionArgs.putBundle(name, sharedElementBundle);
767 }
768
George Mount67d92432014-06-06 13:34:20 -0700769
770 protected void startTransition(Runnable runnable) {
771 if (mIsStartingTransition) {
772 mPendingTransition = runnable;
773 } else {
774 mIsStartingTransition = true;
775 runnable.run();
776 }
777 }
778
George Mount13ccb792014-06-06 17:02:20 -0700779 protected void transitionStarted() {
780 mIsStartingTransition = false;
781 }
782
George Mountfbd45962015-01-26 14:38:19 -0800783 /**
784 * Cancels any pending transitions and returns true if there is a transition is in
785 * the middle of starting.
786 */
787 protected boolean cancelPendingTransitions() {
788 mPendingTransition = null;
789 return mIsStartingTransition;
790 }
791
George Mountfe361d22014-07-08 17:25:25 -0700792 protected void moveSharedElementsToOverlay() {
George Mounteca1ae52014-10-27 14:25:12 -0700793 if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
George Mountb89d5cc2014-08-18 16:50:50 -0700794 return;
795 }
George Mount333b8092014-10-21 15:09:11 -0700796 setSharedElementMatrices();
George Mountfe361d22014-07-08 17:25:25 -0700797 int numSharedElements = mSharedElements.size();
798 ViewGroup decor = getDecor();
799 if (decor != null) {
800 boolean moveWithParent = moveSharedElementWithParent();
George Mount42161c52015-02-24 16:18:09 -0800801 Matrix tempMatrix = new Matrix();
George Mountfe361d22014-07-08 17:25:25 -0700802 for (int i = 0; i < numSharedElements; i++) {
803 View view = mSharedElements.get(i);
George Mount42161c52015-02-24 16:18:09 -0800804 tempMatrix.reset();
805 mSharedElementParentMatrices.get(i).invert(tempMatrix);
806 GhostView.addGhost(view, decor, tempMatrix);
George Mountfe361d22014-07-08 17:25:25 -0700807 ViewGroup parent = (ViewGroup) view.getParent();
808 if (moveWithParent && !isInTransitionGroup(parent, decor)) {
George Mount6e7fb602014-09-04 16:20:20 -0700809 GhostViewListeners listener = new GhostViewListeners(view, parent, decor);
George Mountfe361d22014-07-08 17:25:25 -0700810 parent.getViewTreeObserver().addOnPreDrawListener(listener);
811 mGhostViewListeners.add(listener);
812 }
813 }
814 }
815 }
816
817 protected boolean moveSharedElementWithParent() {
818 return true;
819 }
820
821 public static boolean isInTransitionGroup(ViewParent viewParent, ViewGroup decor) {
822 if (viewParent == decor || !(viewParent instanceof ViewGroup)) {
823 return false;
824 }
825 ViewGroup parent = (ViewGroup) viewParent;
826 if (parent.isTransitionGroup()) {
827 return true;
828 } else {
829 return isInTransitionGroup(parent.getParent(), decor);
830 }
831 }
832
833 protected void moveSharedElementsFromOverlay() {
George Mountb89d5cc2014-08-18 16:50:50 -0700834 int numListeners = mGhostViewListeners.size();
835 for (int i = 0; i < numListeners; i++) {
836 GhostViewListeners listener = mGhostViewListeners.get(i);
837 ViewGroup parent = (ViewGroup) listener.getView().getParent();
838 parent.getViewTreeObserver().removeOnPreDrawListener(listener);
839 }
840 mGhostViewListeners.clear();
841
842 if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
843 return;
844 }
George Mountfe361d22014-07-08 17:25:25 -0700845 ViewGroup decor = getDecor();
846 if (decor != null) {
847 ViewGroupOverlay overlay = decor.getOverlay();
848 int count = mSharedElements.size();
849 for (int i = 0; i < count; i++) {
850 View sharedElement = mSharedElements.get(i);
851 GhostView.removeGhost(sharedElement);
852 }
853 }
George Mountfe361d22014-07-08 17:25:25 -0700854 }
855
856 protected void setGhostVisibility(int visibility) {
857 int numSharedElements = mSharedElements.size();
858 for (int i = 0; i < numSharedElements; i++) {
859 GhostView ghostView = GhostView.getGhost(mSharedElements.get(i));
860 if (ghostView != null) {
861 ghostView.setVisibility(visibility);
862 }
863 }
864 }
865
866 protected void scheduleGhostVisibilityChange(final int visibility) {
George Mount6e7fb602014-09-04 16:20:20 -0700867 final View decorView = getDecor();
George Mount48bd13c2014-09-12 10:54:54 -0700868 if (decorView != null) {
869 decorView.getViewTreeObserver()
870 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
871 @Override
872 public boolean onPreDraw() {
873 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
874 setGhostVisibility(visibility);
875 return true;
876 }
877 });
878 }
George Mountfe361d22014-07-08 17:25:25 -0700879 }
880
George Mount41725de2015-04-09 08:23:05 -0700881 protected boolean isViewsTransitionComplete() {
882 return mViewsTransitionComplete;
883 }
884
885 protected void viewsTransitionComplete() {
886 mViewsTransitionComplete = true;
887 startInputWhenTransitionsComplete();
888 }
889
890 protected void sharedElementTransitionComplete() {
891 mSharedElementTransitionComplete = true;
892 startInputWhenTransitionsComplete();
893 }
894 private void startInputWhenTransitionsComplete() {
895 if (mViewsTransitionComplete && mSharedElementTransitionComplete) {
896 final View decor = getDecor();
897 if (decor != null) {
898 final ViewRootImpl viewRoot = decor.getViewRootImpl();
George Mount5fddd4c2016-01-14 08:24:20 -0800899 if (viewRoot != null) {
900 viewRoot.setPausedForTransition(false);
901 }
George Mount41725de2015-04-09 08:23:05 -0700902 }
903 onTransitionsComplete();
904 }
905 }
906
George Mount80141d12015-07-14 10:03:06 -0700907 protected void pauseInput() {
908 final View decor = getDecor();
909 final ViewRootImpl viewRoot = decor == null ? null : decor.getViewRootImpl();
910 if (viewRoot != null) {
911 viewRoot.setPausedForTransition(true);
912 }
913 }
914
George Mount41725de2015-04-09 08:23:05 -0700915 protected void onTransitionsComplete() {}
916
George Mount67d92432014-06-06 13:34:20 -0700917 protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter {
918 @Override
919 public void onTransitionStart(Transition transition) {
920 mIsStartingTransition = false;
921 Runnable pending = mPendingTransition;
922 mPendingTransition = null;
923 if (pending != null) {
924 startTransition(pending);
925 }
926 }
George Mount25f98a42016-06-14 14:06:39 -0700927
928 @Override
929 public void onTransitionEnd(Transition transition) {
930 transition.removeListener(this);
931 }
George Mount67d92432014-06-06 13:34:20 -0700932 }
933
George Mountc93ca162014-05-23 19:21:36 -0700934 private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
935 for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
936 if (scaleType == SCALE_TYPE_VALUES[i]) {
937 return i;
938 }
939 }
940 return -1;
941 }
942
George Mount62976722016-02-04 16:45:53 -0800943 protected void setTransitioningViewsVisiblity(int visiblity, boolean invalidate) {
944 final int numElements = mTransitioningViews == null ? 0 : mTransitioningViews.size();
945 for (int i = 0; i < numElements; i++) {
946 final View view = mTransitioningViews.get(i);
947 view.setTransitionVisibility(visiblity);
948 if (invalidate) {
949 view.invalidate();
950 }
951 }
952 }
953
George Mount1349bb92016-03-22 13:41:57 -0700954 /**
955 * Blocks suppressLayout from Visibility transitions. It is ok to suppress the layout,
956 * but we don't want to force the layout when suppressLayout becomes false. This leads
957 * to visual glitches.
958 */
959 private static void noLayoutSuppressionForVisibilityTransitions(Transition transition) {
960 if (transition instanceof Visibility) {
961 final Visibility visibility = (Visibility) transition;
962 visibility.setSuppressLayout(false);
963 } else if (transition instanceof TransitionSet) {
964 final TransitionSet set = (TransitionSet) transition;
965 final int count = set.getTransitionCount();
966 for (int i = 0; i < count; i++) {
967 noLayoutSuppressionForVisibilityTransitions(set.getTransitionAt(i));
968 }
969 }
970 }
971
George Mount31a21722014-03-24 17:44:36 -0700972 private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
973 private Rect mEpicenter;
974
975 public void setEpicenter(Rect epicenter) { mEpicenter = epicenter; }
976
977 @Override
George Mountdc21d3b2014-06-05 09:42:48 -0700978 public Rect onGetEpicenter(Transition transition) {
George Mount31a21722014-03-24 17:44:36 -0700979 return mEpicenter;
980 }
981 }
George Mount800d72b2014-05-19 07:09:00 -0700982
George Mountfe361d22014-07-08 17:25:25 -0700983 private static class GhostViewListeners implements ViewTreeObserver.OnPreDrawListener {
984 private View mView;
985 private ViewGroup mDecor;
George Mount6e7fb602014-09-04 16:20:20 -0700986 private View mParent;
George Mountfe361d22014-07-08 17:25:25 -0700987 private Matrix mMatrix = new Matrix();
988
George Mount6e7fb602014-09-04 16:20:20 -0700989 public GhostViewListeners(View view, View parent, ViewGroup decor) {
George Mountfe361d22014-07-08 17:25:25 -0700990 mView = view;
George Mount6e7fb602014-09-04 16:20:20 -0700991 mParent = parent;
George Mountfe361d22014-07-08 17:25:25 -0700992 mDecor = decor;
993 }
994
995 public View getView() {
996 return mView;
997 }
998
999 @Override
1000 public boolean onPreDraw() {
George Mountfe361d22014-07-08 17:25:25 -07001001 GhostView ghostView = GhostView.getGhost(mView);
1002 if (ghostView == null) {
George Mount6e7fb602014-09-04 16:20:20 -07001003 mParent.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountfe361d22014-07-08 17:25:25 -07001004 } else {
1005 GhostView.calculateMatrix(mView, mDecor, mMatrix);
1006 ghostView.setMatrix(mMatrix);
1007 }
1008 return true;
1009 }
1010 }
1011
Dake Guc18f4cc2014-07-11 17:48:37 -07001012 static class SharedElementOriginalState {
1013 int mLeft;
1014 int mTop;
1015 int mRight;
1016 int mBottom;
1017 int mMeasuredWidth;
1018 int mMeasuredHeight;
1019 ImageView.ScaleType mScaleType;
1020 Matrix mMatrix;
George Mount26c82b62014-08-08 15:43:59 -07001021 float mTranslationZ;
1022 float mElevation;
Dake Guc18f4cc2014-07-11 17:48:37 -07001023 }
George Mount31a21722014-03-24 17:44:36 -07001024}