blob: 198bfb0a414b4bee7abe61053582a14c60df77fd [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;
28import android.util.ArrayMap;
George Mountfe361d22014-07-08 17:25:25 -070029import android.view.GhostView;
George Mount31a21722014-03-24 17:44:36 -070030import android.view.View;
31import android.view.ViewGroup;
George Mountfe361d22014-07-08 17:25:25 -070032import android.view.ViewGroupOverlay;
33import android.view.ViewParent;
George Mount41725de2015-04-09 08:23:05 -070034import android.view.ViewRootImpl;
George Mountc93ca162014-05-23 19:21:36 -070035import android.view.ViewTreeObserver;
George Mount31a21722014-03-24 17:44:36 -070036import android.view.Window;
George Mountcaa03102014-04-15 09:01:32 -070037import android.widget.ImageView;
George Mount31a21722014-03-24 17:44:36 -070038
39import java.util.ArrayList;
40import java.util.Collection;
41
42/**
43 * Base class for ExitTransitionCoordinator and EnterTransitionCoordinator, classes
44 * that manage activity transitions and the communications coordinating them between
45 * Activities. The ExitTransitionCoordinator is created in the
46 * ActivityOptions#makeSceneTransitionAnimation. The EnterTransitionCoordinator
47 * is created by ActivityOptions#createEnterActivityTransition by Activity when the window is
48 * attached.
49 *
50 * Typical startActivity goes like this:
51 * 1) ExitTransitionCoordinator created with ActivityOptions#makeSceneTransitionAnimation
52 * 2) Activity#startActivity called and that calls startExit() through
53 * ActivityOptions#dispatchStartExit
54 * - Exit transition starts by setting transitioning Views to INVISIBLE
55 * 3) Launched Activity starts, creating an EnterTransitionCoordinator.
56 * - The Window is made translucent
57 * - The Window background alpha is set to 0
58 * - The transitioning views are made INVISIBLE
59 * - MSG_SET_LISTENER is sent back to the ExitTransitionCoordinator.
60 * 4) The shared element transition completes.
61 * - MSG_TAKE_SHARED_ELEMENTS is sent to the EnterTransitionCoordinator
62 * 5) The MSG_TAKE_SHARED_ELEMENTS is received by the EnterTransitionCoordinator.
63 * - Shared elements are made VISIBLE
64 * - Shared elements positions and size are set to match the end state of the calling
65 * Activity.
66 * - The shared element transition is started
67 * - If the window allows overlapping transitions, the views transition is started by setting
68 * the entering Views to VISIBLE and the background alpha is animated to opaque.
69 * - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
70 * 6) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
71 * - The shared elements are made INVISIBLE
72 * 7) The exit transition completes in the calling Activity.
73 * - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
74 * 8) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
75 * - If the window doesn't allow overlapping enter transitions, the enter transition is started
76 * by setting entering views to VISIBLE and the background is animated to opaque.
77 * 9) The background opacity animation completes.
78 * - The window is made opaque
79 * 10) The calling Activity gets an onStop() call
80 * - onActivityStopped() is called and all exited Views are made VISIBLE.
81 *
Craig Mautner73f843d2014-05-19 09:42:28 -070082 * Typical finishAfterTransition goes like this:
83 * 1) finishAfterTransition() creates an ExitTransitionCoordinator and calls startExit()
George Mount62ab9b72014-05-02 13:51:17 -070084 * - The Window start transitioning to Translucent with a new ActivityOptions.
George Mount31a21722014-03-24 17:44:36 -070085 * - If no background exists, a black background is substituted
George Mount31a21722014-03-24 17:44:36 -070086 * - The shared elements in the scene are matched against those shared elements
87 * that were sent by comparing the names.
88 * - The exit transition is started by setting Views to INVISIBLE.
George Mount62ab9b72014-05-02 13:51:17 -070089 * 2) The ActivityOptions is received by the Activity and an EnterTransitionCoordinator is created.
George Mount31a21722014-03-24 17:44:36 -070090 * - All transitioning views are made VISIBLE to reverse what was done when onActivityStopped()
91 * was called
92 * 3) The Window is made translucent and a callback is received
93 * - The background alpha is animated to 0
94 * 4) The background alpha animation completes
95 * 5) The shared element transition completes
96 * - After both 4 & 5 complete, MSG_TAKE_SHARED_ELEMENTS is sent to the
George Mount62ab9b72014-05-02 13:51:17 -070097 * EnterTransitionCoordinator
98 * 6) MSG_TAKE_SHARED_ELEMENTS is received by EnterTransitionCoordinator
George Mount31a21722014-03-24 17:44:36 -070099 * - Shared elements are made VISIBLE
100 * - Shared elements positions and size are set to match the end state of the calling
101 * Activity.
102 * - The shared element transition is started
103 * - If the window allows overlapping transitions, the views transition is started by setting
104 * the entering Views to VISIBLE.
George Mount62ab9b72014-05-02 13:51:17 -0700105 * - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
106 * 7) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
George Mount31a21722014-03-24 17:44:36 -0700107 * - The shared elements are made INVISIBLE
108 * 8) The exit transition completes in the finishing Activity.
George Mount62ab9b72014-05-02 13:51:17 -0700109 * - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
George Mount31a21722014-03-24 17:44:36 -0700110 * - finish() is called on the exiting Activity
George Mount62ab9b72014-05-02 13:51:17 -0700111 * 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
George Mount31a21722014-03-24 17:44:36 -0700112 * - If the window doesn't allow overlapping enter transitions, the enter transition is started
113 * by setting entering views to VISIBLE.
114 */
115abstract class ActivityTransitionCoordinator extends ResultReceiver {
116 private static final String TAG = "ActivityTransitionCoordinator";
117
118 /**
George Mount31a21722014-03-24 17:44:36 -0700119 * For Activity transitions, the called Activity's listener to receive calls
120 * when transitions complete.
121 */
George Mount62ab9b72014-05-02 13:51:17 -0700122 static final String KEY_REMOTE_RECEIVER = "android:remoteReceiver";
George Mount31a21722014-03-24 17:44:36 -0700123
Dake Gu7bf379c2014-07-15 16:29:38 -0700124 protected static final String KEY_SCREEN_LEFT = "shared_element:screenLeft";
125 protected static final String KEY_SCREEN_TOP = "shared_element:screenTop";
126 protected static final String KEY_SCREEN_RIGHT = "shared_element:screenRight";
127 protected static final String KEY_SCREEN_BOTTOM= "shared_element:screenBottom";
George Mount62ab9b72014-05-02 13:51:17 -0700128 protected static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
George Mount480ca822014-08-08 16:35:48 -0700129 protected static final String KEY_SNAPSHOT = "shared_element:bitmap";
George Mount62ab9b72014-05-02 13:51:17 -0700130 protected static final String KEY_SCALE_TYPE = "shared_element:scaleType";
131 protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
George Mount26c82b62014-08-08 15:43:59 -0700132 protected static final String KEY_ELEVATION = "shared_element:elevation";
George Mount080443b2014-05-05 10:47:00 -0700133
George Mount62ab9b72014-05-02 13:51:17 -0700134 protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
George Mount31a21722014-03-24 17:44:36 -0700135
136 /**
137 * Sent by the exiting coordinator (either EnterTransitionCoordinator
138 * or ExitTransitionCoordinator) after the shared elements have
139 * become stationary (shared element transition completes). This tells
140 * the remote coordinator to take control of the shared elements and
141 * that animations may begin. The remote Activity won't start entering
142 * until this message is received, but may wait for
143 * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
144 */
George Mount62ab9b72014-05-02 13:51:17 -0700145 public static final int MSG_SET_REMOTE_RECEIVER = 100;
George Mount31a21722014-03-24 17:44:36 -0700146
147 /**
148 * Sent by the entering coordinator to tell the exiting coordinator
149 * to hide its shared elements after it has started its shared
150 * element transition. This is temporary until the
151 * interlock of shared elements is figured out.
152 */
153 public static final int MSG_HIDE_SHARED_ELEMENTS = 101;
154
155 /**
George Mount31a21722014-03-24 17:44:36 -0700156 * Sent by the exiting coordinator (either EnterTransitionCoordinator
157 * or ExitTransitionCoordinator) after the shared elements have
158 * become stationary (shared element transition completes). This tells
159 * the remote coordinator to take control of the shared elements and
160 * that animations may begin. The remote Activity won't start entering
161 * until this message is received, but may wait for
162 * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
163 */
George Mount62ab9b72014-05-02 13:51:17 -0700164 public static final int MSG_TAKE_SHARED_ELEMENTS = 103;
George Mount31a21722014-03-24 17:44:36 -0700165
166 /**
167 * Sent by the exiting coordinator (either
168 * EnterTransitionCoordinator or ExitTransitionCoordinator) after
169 * the exiting Views have finished leaving the scene. This will
170 * be ignored if allowOverlappingTransitions() is true on the
171 * remote coordinator. If it is false, it will trigger the enter
172 * transition to start.
173 */
George Mount62ab9b72014-05-02 13:51:17 -0700174 public static final int MSG_EXIT_TRANSITION_COMPLETE = 104;
George Mount31a21722014-03-24 17:44:36 -0700175
176 /**
177 * Sent by Activity#startActivity to begin the exit transition.
178 */
George Mount62ab9b72014-05-02 13:51:17 -0700179 public static final int MSG_START_EXIT_TRANSITION = 105;
George Mount31a21722014-03-24 17:44:36 -0700180
George Mount62ab9b72014-05-02 13:51:17 -0700181 /**
182 * It took too long for a message from the entering Activity, so we canceled the transition.
183 */
184 public static final int MSG_CANCEL = 106;
George Mount31a21722014-03-24 17:44:36 -0700185
George Mountc93ca162014-05-23 19:21:36 -0700186 /**
187 * When returning, this is the destination location for the shared element.
188 */
189 public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
190
George Mounta0a02602014-06-20 18:22:26 -0700191 private Window mWindow;
George Mount62ab9b72014-05-02 13:51:17 -0700192 final protected ArrayList<String> mAllSharedElementNames;
193 final protected ArrayList<View> mSharedElements = new ArrayList<View>();
194 final protected ArrayList<String> mSharedElementNames = new ArrayList<String>();
George Mountb694e082014-09-12 07:34:52 -0700195 protected ArrayList<View> mTransitioningViews = new ArrayList<View>();
George Mount65580562014-08-29 08:15:48 -0700196 protected SharedElementCallback mListener;
George Mount62ab9b72014-05-02 13:51:17 -0700197 protected ResultReceiver mResultReceiver;
198 final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
George Mountc9b6df82014-05-21 15:07:04 -0700199 final protected boolean mIsReturning;
George Mount67d92432014-06-06 13:34:20 -0700200 private Runnable mPendingTransition;
201 private boolean mIsStartingTransition;
George Mountfe361d22014-07-08 17:25:25 -0700202 private ArrayList<GhostViewListeners> mGhostViewListeners =
203 new ArrayList<GhostViewListeners>();
George Mount0f0c4732014-09-05 13:47:47 -0700204 private ArrayMap<View, Float> mOriginalAlphas = new ArrayMap<View, Float>();
George Mount333b8092014-10-21 15:09:11 -0700205 private ArrayList<Matrix> mSharedElementParentMatrices;
George Mount41725de2015-04-09 08:23:05 -0700206 private boolean mSharedElementTransitionComplete;
207 private boolean mViewsTransitionComplete;
George Mount67d92432014-06-06 13:34:20 -0700208
George Mount8c2614c2014-06-10 11:12:01 -0700209 public ActivityTransitionCoordinator(Window window,
210 ArrayList<String> allSharedElementNames,
George Mount65580562014-08-29 08:15:48 -0700211 SharedElementCallback listener, boolean isReturning) {
George Mount31a21722014-03-24 17:44:36 -0700212 super(new Handler());
213 mWindow = window;
George Mount62ab9b72014-05-02 13:51:17 -0700214 mListener = listener;
215 mAllSharedElementNames = allSharedElementNames;
George Mountc9b6df82014-05-21 15:07:04 -0700216 mIsReturning = isReturning;
George Mount8c2614c2014-06-10 11:12:01 -0700217 }
218
George Mount1fecfb22014-06-18 14:55:55 -0700219 protected void viewsReady(ArrayMap<String, View> sharedElements) {
George Mountce725a42014-08-27 16:10:46 -0700220 sharedElements.retainAll(mAllSharedElementNames);
George Mount1732f522014-09-17 16:59:36 -0700221 if (mListener != null) {
222 mListener.onMapSharedElements(mAllSharedElementNames, sharedElements);
223 }
George Mount333b8092014-10-21 15:09:11 -0700224 setSharedElements(sharedElements);
George Mountb694e082014-09-12 07:34:52 -0700225 if (getViewsTransition() != null && mTransitioningViews != null) {
George Mount48bd13c2014-09-12 10:54:54 -0700226 ViewGroup decorView = getDecor();
227 if (decorView != null) {
228 decorView.captureTransitioningViews(mTransitioningViews);
229 }
George Mount62ab9b72014-05-02 13:51:17 -0700230 mTransitioningViews.removeAll(mSharedElements);
George Mount31a21722014-03-24 17:44:36 -0700231 }
George Mount62ab9b72014-05-02 13:51:17 -0700232 setEpicenter();
George Mount31a21722014-03-24 17:44:36 -0700233 }
234
George Mount333b8092014-10-21 15:09:11 -0700235 /**
236 * Iterates over the shared elements and adds them to the members in order.
237 * Shared elements that are nested in other shared elements are placed after the
238 * elements that they are nested in. This means that layout ordering can be done
239 * from first to last.
240 *
241 * @param sharedElements The map of transition names to shared elements to set into
242 * the member fields.
243 */
244 private void setSharedElements(ArrayMap<String, View> sharedElements) {
245 boolean isFirstRun = true;
246 while (!sharedElements.isEmpty()) {
247 final int numSharedElements = sharedElements.size();
248 for (int i = numSharedElements - 1; i >= 0; i--) {
249 final View view = sharedElements.valueAt(i);
250 final String name = sharedElements.keyAt(i);
251 if (isFirstRun && (view == null || !view.isAttachedToWindow() || name == null)) {
252 sharedElements.removeAt(i);
George Mount42161c52015-02-24 16:18:09 -0800253 } else if (!isNested(view, sharedElements)) {
254 mSharedElementNames.add(name);
255 mSharedElements.add(view);
256 sharedElements.removeAt(i);
George Mount333b8092014-10-21 15:09:11 -0700257 }
258 }
259 isFirstRun = false;
260 }
261 }
262
263 /**
264 * Returns true when view is nested in any of the values of sharedElements.
265 */
266 private static boolean isNested(View view, ArrayMap<String, View> sharedElements) {
267 ViewParent parent = view.getParent();
268 boolean isNested = false;
269 while (parent instanceof View) {
270 View parentView = (View) parent;
271 if (sharedElements.containsValue(parentView)) {
272 isNested = true;
273 break;
274 }
275 parent = parentView.getParent();
276 }
277 return isNested;
278 }
279
George Mount60625b02014-06-24 07:46:23 -0700280 protected void stripOffscreenViews() {
George Mountb694e082014-09-12 07:34:52 -0700281 if (mTransitioningViews == null) {
282 return;
283 }
George Mount60625b02014-06-24 07:46:23 -0700284 Rect r = new Rect();
285 for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
286 View view = mTransitioningViews.get(i);
287 if (!view.getGlobalVisibleRect(r)) {
288 mTransitioningViews.remove(i);
George Mount653ea662014-09-12 13:56:32 -0700289 showView(view, true);
George Mount60625b02014-06-24 07:46:23 -0700290 }
291 }
292 }
293
George Mount31a21722014-03-24 17:44:36 -0700294 protected Window getWindow() {
295 return mWindow;
296 }
297
George Mounta2bbbb32014-08-12 10:16:20 -0700298 public ViewGroup getDecor() {
George Mount31a21722014-03-24 17:44:36 -0700299 return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView();
300 }
301
George Mount62ab9b72014-05-02 13:51:17 -0700302 /**
303 * Sets the transition epicenter to the position of the first shared element.
304 */
305 protected void setEpicenter() {
306 View epicenter = null;
George Mountc6186bf2014-09-04 16:33:59 -0700307 if (!mAllSharedElementNames.isEmpty() && !mSharedElementNames.isEmpty()) {
308 int index = mSharedElementNames.indexOf(mAllSharedElementNames.get(0));
309 if (index >= 0) {
310 epicenter = mSharedElements.get(index);
311 }
George Mount31a21722014-03-24 17:44:36 -0700312 }
George Mount62ab9b72014-05-02 13:51:17 -0700313 setEpicenter(epicenter);
George Mount31a21722014-03-24 17:44:36 -0700314 }
315
George Mount62ab9b72014-05-02 13:51:17 -0700316 private void setEpicenter(View view) {
317 if (view == null) {
318 mEpicenterCallback.setEpicenter(null);
George Mount31a21722014-03-24 17:44:36 -0700319 } else {
George Mount8e43d6d2014-06-05 17:25:46 -0700320 Rect epicenter = new Rect();
321 view.getBoundsOnScreen(epicenter);
George Mount62ab9b72014-05-02 13:51:17 -0700322 mEpicenterCallback.setEpicenter(epicenter);
George Mount31a21722014-03-24 17:44:36 -0700323 }
324 }
325
George Mount62ab9b72014-05-02 13:51:17 -0700326 public ArrayList<String> getAcceptedNames() {
327 return mSharedElementNames;
George Mount31a21722014-03-24 17:44:36 -0700328 }
329
George Mount62ab9b72014-05-02 13:51:17 -0700330 public ArrayList<String> getMappedNames() {
331 ArrayList<String> names = new ArrayList<String>(mSharedElements.size());
332 for (int i = 0; i < mSharedElements.size(); i++) {
George Mount0a2ae002014-06-23 14:57:27 +0000333 names.add(mSharedElements.get(i).getTransitionName());
George Mount31a21722014-03-24 17:44:36 -0700334 }
George Mount62ab9b72014-05-02 13:51:17 -0700335 return names;
George Mount31a21722014-03-24 17:44:36 -0700336 }
337
George Mount700db2a2014-07-07 17:17:49 -0700338 public ArrayList<View> copyMappedViews() {
339 return new ArrayList<View>(mSharedElements);
George Mount1fecfb22014-06-18 14:55:55 -0700340 }
341
George Mount8c2614c2014-06-10 11:12:01 -0700342 public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
343
George Mount88815022014-06-25 14:33:54 -0700344 protected Transition setTargets(Transition transition, boolean add) {
345 if (transition == null || (add &&
346 (mTransitioningViews == null || mTransitioningViews.isEmpty()))) {
George Mount31a21722014-03-24 17:44:36 -0700347 return null;
348 }
George Mountd4c3c912014-06-09 12:31:34 -0700349 // Add the targets to a set containing transition so that transition
350 // remains unaffected. We don't want to modify the targets of transition itself.
George Mount31a21722014-03-24 17:44:36 -0700351 TransitionSet set = new TransitionSet();
George Mount88815022014-06-25 14:33:54 -0700352 if (mTransitioningViews != null) {
353 for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
354 View view = mTransitioningViews.get(i);
355 if (add) {
356 set.addTarget(view);
357 } else {
358 set.excludeTarget(view, true);
359 }
George Mount31a21722014-03-24 17:44:36 -0700360 }
361 }
George Mountd4c3c912014-06-09 12:31:34 -0700362 // By adding the transition after addTarget, we prevent addTarget from
363 // affecting transition.
364 set.addTransition(transition);
George Mountfe361d22014-07-08 17:25:25 -0700365
366 if (!add && mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
367 // Allow children of excluded transitioning views, but not the views themselves
368 set = new TransitionSet().addTransition(set);
369 }
370
George Mount31a21722014-03-24 17:44:36 -0700371 return set;
372 }
373
George Mount88815022014-06-25 14:33:54 -0700374 protected Transition configureTransition(Transition transition,
375 boolean includeTransitioningViews) {
George Mount31a21722014-03-24 17:44:36 -0700376 if (transition != null) {
George Mount62ab9b72014-05-02 13:51:17 -0700377 transition = transition.clone();
378 transition.setEpicenterCallback(mEpicenterCallback);
George Mount88815022014-06-25 14:33:54 -0700379 transition = setTargets(transition, includeTransitioningViews);
George Mount31a21722014-03-24 17:44:36 -0700380 }
381 return transition;
382 }
383
George Mount62ab9b72014-05-02 13:51:17 -0700384 protected static Transition mergeTransitions(Transition transition1, Transition transition2) {
385 if (transition1 == null) {
386 return transition2;
387 } else if (transition2 == null) {
388 return transition1;
389 } else {
390 TransitionSet transitionSet = new TransitionSet();
391 transitionSet.addTransition(transition1);
392 transitionSet.addTransition(transition2);
393 return transitionSet;
George Mountcaa03102014-04-15 09:01:32 -0700394 }
George Mountcaa03102014-04-15 09:01:32 -0700395 }
396
George Mount1fecfb22014-06-18 14:55:55 -0700397 protected ArrayMap<String, View> mapSharedElements(ArrayList<String> accepted,
398 ArrayList<View> localViews) {
399 ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
George Mountce725a42014-08-27 16:10:46 -0700400 if (accepted != null) {
401 for (int i = 0; i < accepted.size(); i++) {
402 sharedElements.put(accepted.get(i), localViews.get(i));
George Mount62ab9b72014-05-02 13:51:17 -0700403 }
George Mountce725a42014-08-27 16:10:46 -0700404 } else {
George Mount48bd13c2014-09-12 10:54:54 -0700405 ViewGroup decorView = getDecor();
406 if (decorView != null) {
407 decorView.findNamedViews(sharedElements);
408 }
George Mount1fecfb22014-06-18 14:55:55 -0700409 }
410 return sharedElements;
411 }
412
George Mount62ab9b72014-05-02 13:51:17 -0700413 protected void setResultReceiver(ResultReceiver resultReceiver) {
414 mResultReceiver = resultReceiver;
George Mount080443b2014-05-05 10:47:00 -0700415 }
416
George Mounta712e8c2014-05-20 15:10:20 -0700417 protected abstract Transition getViewsTransition();
George Mount080443b2014-05-05 10:47:00 -0700418
George Mountfe361d22014-07-08 17:25:25 -0700419 private void setSharedElementState(View view, String name, Bundle transitionArgs,
Dake Gu7bf379c2014-07-15 16:29:38 -0700420 Matrix tempMatrix, RectF tempRect, int[] decorLoc) {
George Mountc93ca162014-05-23 19:21:36 -0700421 Bundle sharedElementBundle = transitionArgs.getBundle(name);
422 if (sharedElementBundle == null) {
423 return;
424 }
425
426 if (view instanceof ImageView) {
427 int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
428 if (scaleTypeInt >= 0) {
429 ImageView imageView = (ImageView) view;
430 ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
431 imageView.setScaleType(scaleType);
432 if (scaleType == ImageView.ScaleType.MATRIX) {
433 float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
Dake Gu7bf379c2014-07-15 16:29:38 -0700434 tempMatrix.setValues(matrixValues);
435 imageView.setImageMatrix(tempMatrix);
George Mountc93ca162014-05-23 19:21:36 -0700436 }
437 }
438 }
439
440 float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
441 view.setTranslationZ(z);
George Mount26c82b62014-08-08 15:43:59 -0700442 float elevation = sharedElementBundle.getFloat(KEY_ELEVATION);
443 view.setElevation(elevation);
George Mountc93ca162014-05-23 19:21:36 -0700444
Dake Gu7bf379c2014-07-15 16:29:38 -0700445 float left = sharedElementBundle.getFloat(KEY_SCREEN_LEFT);
446 float top = sharedElementBundle.getFloat(KEY_SCREEN_TOP);
447 float right = sharedElementBundle.getFloat(KEY_SCREEN_RIGHT);
448 float bottom = sharedElementBundle.getFloat(KEY_SCREEN_BOTTOM);
George Mountc93ca162014-05-23 19:21:36 -0700449
Dake Gu7bf379c2014-07-15 16:29:38 -0700450 if (decorLoc != null) {
451 left -= decorLoc[0];
452 top -= decorLoc[1];
George Mount480ca822014-08-08 16:35:48 -0700453 right -= decorLoc[0];
454 bottom -= decorLoc[1];
Dake Gu7bf379c2014-07-15 16:29:38 -0700455 } else {
456 // Find the location in the view's parent
George Mountfe361d22014-07-08 17:25:25 -0700457 getSharedElementParentMatrix(view, tempMatrix);
Dake Gu7bf379c2014-07-15 16:29:38 -0700458 tempRect.set(left, top, right, bottom);
459 tempMatrix.mapRect(tempRect);
460
461 float leftInParent = tempRect.left;
462 float topInParent = tempRect.top;
463
464 // Find the size of the view
465 view.getInverseMatrix().mapRect(tempRect);
466 float width = tempRect.width();
467 float height = tempRect.height();
468
469 // Now determine the offset due to view transform:
470 view.setLeft(0);
471 view.setTop(0);
472 view.setRight(Math.round(width));
473 view.setBottom(Math.round(height));
474 tempRect.set(0, 0, width, height);
475 view.getMatrix().mapRect(tempRect);
476
Doris Liu18c2b0e2015-05-01 11:02:01 -0700477 left = leftInParent - tempRect.left;
478 top = topInParent - tempRect.top;
Dake Gu7bf379c2014-07-15 16:29:38 -0700479 right = left + width;
480 bottom = top + height;
481 }
482
483 int x = Math.round(left);
484 int y = Math.round(top);
485 int width = Math.round(right) - x;
486 int height = Math.round(bottom) - y;
George Mountc93ca162014-05-23 19:21:36 -0700487 int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
488 int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
489 view.measure(widthSpec, heightSpec);
490
Dake Gu7bf379c2014-07-15 16:29:38 -0700491 view.layout(x, y, x + width, y + height);
George Mountc93ca162014-05-23 19:21:36 -0700492 }
493
George Mount333b8092014-10-21 15:09:11 -0700494 private void setSharedElementMatrices() {
495 int numSharedElements = mSharedElements.size();
496 if (numSharedElements > 0) {
497 mSharedElementParentMatrices = new ArrayList<Matrix>(numSharedElements);
498 }
499 for (int i = 0; i < numSharedElements; i++) {
500 View view = mSharedElements.get(i);
501
502 // Find the location in the view's parent
503 ViewGroup parent = (ViewGroup) view.getParent();
504 Matrix matrix = new Matrix();
505 parent.transformMatrixToLocal(matrix);
Doris Liu18c2b0e2015-05-01 11:02:01 -0700506 matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
George Mount333b8092014-10-21 15:09:11 -0700507 mSharedElementParentMatrices.add(matrix);
508 }
509 }
510
511 private void getSharedElementParentMatrix(View view, Matrix matrix) {
George Mount42161c52015-02-24 16:18:09 -0800512 final int index = mSharedElementParentMatrices == null ? -1
513 : mSharedElements.indexOf(view);
514 if (index < 0) {
George Mount333b8092014-10-21 15:09:11 -0700515 matrix.reset();
516 ViewParent viewParent = view.getParent();
517 if (viewParent instanceof ViewGroup) {
518 // Find the location in the view's parent
519 ViewGroup parent = (ViewGroup) viewParent;
520 parent.transformMatrixToLocal(matrix);
Doris Liu18c2b0e2015-05-01 11:02:01 -0700521 matrix.postTranslate(parent.getScrollX(), parent.getScrollY());
George Mount333b8092014-10-21 15:09:11 -0700522 }
George Mount42161c52015-02-24 16:18:09 -0800523 } else {
524 // The indices of mSharedElementParentMatrices matches the
525 // mSharedElement matrices.
526 Matrix parentMatrix = mSharedElementParentMatrices.get(index);
527 matrix.set(parentMatrix);
George Mount333b8092014-10-21 15:09:11 -0700528 }
George Mountfe361d22014-07-08 17:25:25 -0700529 }
530
Dake Guc18f4cc2014-07-11 17:48:37 -0700531 protected ArrayList<SharedElementOriginalState> setSharedElementState(
George Mountc93ca162014-05-23 19:21:36 -0700532 Bundle sharedElementState, final ArrayList<View> snapshots) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700533 ArrayList<SharedElementOriginalState> originalImageState =
534 new ArrayList<SharedElementOriginalState>();
George Mountc93ca162014-05-23 19:21:36 -0700535 if (sharedElementState != null) {
Dake Gu7bf379c2014-07-15 16:29:38 -0700536 Matrix tempMatrix = new Matrix();
537 RectF tempRect = new RectF();
George Mount1732f522014-09-17 16:59:36 -0700538 final int numSharedElements = mSharedElements.size();
539 for (int i = 0; i < numSharedElements; i++) {
George Mountc93ca162014-05-23 19:21:36 -0700540 View sharedElement = mSharedElements.get(i);
541 String name = mSharedElementNames.get(i);
Dake Guc18f4cc2014-07-11 17:48:37 -0700542 SharedElementOriginalState originalState = getOldSharedElementState(sharedElement,
George Mountc93ca162014-05-23 19:21:36 -0700543 name, sharedElementState);
Dake Guc18f4cc2014-07-11 17:48:37 -0700544 originalImageState.add(originalState);
Dake Gu7bf379c2014-07-15 16:29:38 -0700545 setSharedElementState(sharedElement, name, sharedElementState,
546 tempMatrix, tempRect, null);
George Mountc93ca162014-05-23 19:21:36 -0700547 }
548 }
George Mount1732f522014-09-17 16:59:36 -0700549 if (mListener != null) {
550 mListener.onSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
551 }
George Mountfe361d22014-07-08 17:25:25 -0700552 return originalImageState;
553 }
George Mountc93ca162014-05-23 19:21:36 -0700554
George Mountfe361d22014-07-08 17:25:25 -0700555 protected void notifySharedElementEnd(ArrayList<View> snapshots) {
George Mount1732f522014-09-17 16:59:36 -0700556 if (mListener != null) {
557 mListener.onSharedElementEnd(mSharedElementNames, mSharedElements, snapshots);
558 }
George Mountfe361d22014-07-08 17:25:25 -0700559 }
560
561 protected void scheduleSetSharedElementEnd(final ArrayList<View> snapshots) {
George Mount6e7fb602014-09-04 16:20:20 -0700562 final View decorView = getDecor();
George Mount48bd13c2014-09-12 10:54:54 -0700563 if (decorView != null) {
564 decorView.getViewTreeObserver().addOnPreDrawListener(
565 new ViewTreeObserver.OnPreDrawListener() {
566 @Override
567 public boolean onPreDraw() {
568 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
569 notifySharedElementEnd(snapshots);
570 return true;
571 }
George Mountc93ca162014-05-23 19:21:36 -0700572 }
George Mount48bd13c2014-09-12 10:54:54 -0700573 );
574 }
George Mountc93ca162014-05-23 19:21:36 -0700575 }
576
Dake Guc18f4cc2014-07-11 17:48:37 -0700577 private static SharedElementOriginalState getOldSharedElementState(View view, String name,
George Mountc93ca162014-05-23 19:21:36 -0700578 Bundle transitionArgs) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700579
580 SharedElementOriginalState state = new SharedElementOriginalState();
581 state.mLeft = view.getLeft();
582 state.mTop = view.getTop();
583 state.mRight = view.getRight();
584 state.mBottom = view.getBottom();
585 state.mMeasuredWidth = view.getMeasuredWidth();
586 state.mMeasuredHeight = view.getMeasuredHeight();
George Mount26c82b62014-08-08 15:43:59 -0700587 state.mTranslationZ = view.getTranslationZ();
588 state.mElevation = view.getElevation();
George Mountc93ca162014-05-23 19:21:36 -0700589 if (!(view instanceof ImageView)) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700590 return state;
George Mountc93ca162014-05-23 19:21:36 -0700591 }
592 Bundle bundle = transitionArgs.getBundle(name);
593 if (bundle == null) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700594 return state;
George Mountc93ca162014-05-23 19:21:36 -0700595 }
596 int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
597 if (scaleTypeInt < 0) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700598 return state;
George Mountc93ca162014-05-23 19:21:36 -0700599 }
600
601 ImageView imageView = (ImageView) view;
Dake Guc18f4cc2014-07-11 17:48:37 -0700602 state.mScaleType = imageView.getScaleType();
603 if (state.mScaleType == ImageView.ScaleType.MATRIX) {
604 state.mMatrix = new Matrix(imageView.getImageMatrix());
George Mountc93ca162014-05-23 19:21:36 -0700605 }
Dake Guc18f4cc2014-07-11 17:48:37 -0700606 return state;
George Mountc93ca162014-05-23 19:21:36 -0700607 }
608
609 protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
610 int numSharedElements = names.size();
George Mountc93ca162014-05-23 19:21:36 -0700611 ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
George Mount7c479f52014-10-15 16:21:51 -0700612 if (numSharedElements == 0) {
613 return snapshots;
614 }
George Mountc93ca162014-05-23 19:21:36 -0700615 Context context = getWindow().getContext();
Dake Gu7bf379c2014-07-15 16:29:38 -0700616 int[] decorLoc = new int[2];
George Mount48bd13c2014-09-12 10:54:54 -0700617 ViewGroup decorView = getDecor();
618 if (decorView != null) {
619 decorView.getLocationOnScreen(decorLoc);
620 }
George Mountca5094a2014-10-30 11:28:00 -0700621 Matrix tempMatrix = new Matrix();
George Mountc93ca162014-05-23 19:21:36 -0700622 for (String name: names) {
623 Bundle sharedElementBundle = state.getBundle(name);
George Mount92692c02014-12-01 16:44:05 -0800624 View snapshot = null;
George Mountc93ca162014-05-23 19:21:36 -0700625 if (sharedElementBundle != null) {
George Mount480ca822014-08-08 16:35:48 -0700626 Parcelable parcelable = sharedElementBundle.getParcelable(KEY_SNAPSHOT);
George Mount1732f522014-09-17 16:59:36 -0700627 if (parcelable != null && mListener != null) {
George Mount65580562014-08-29 08:15:48 -0700628 snapshot = mListener.onCreateSnapshotView(context, parcelable);
George Mountc93ca162014-05-23 19:21:36 -0700629 }
George Mount480ca822014-08-08 16:35:48 -0700630 if (snapshot != null) {
George Mountca5094a2014-10-30 11:28:00 -0700631 setSharedElementState(snapshot, name, state, tempMatrix, null, decorLoc);
George Mount480ca822014-08-08 16:35:48 -0700632 }
George Mountc93ca162014-05-23 19:21:36 -0700633 }
George Mount92692c02014-12-01 16:44:05 -0800634 // Even null snapshots are added so they remain in the same order as shared elements.
635 snapshots.add(snapshot);
George Mountc93ca162014-05-23 19:21:36 -0700636 }
637 return snapshots;
638 }
639
Dake Guc18f4cc2014-07-11 17:48:37 -0700640 protected static void setOriginalSharedElementState(ArrayList<View> sharedElements,
641 ArrayList<SharedElementOriginalState> originalState) {
George Mountc93ca162014-05-23 19:21:36 -0700642 for (int i = 0; i < originalState.size(); i++) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700643 View view = sharedElements.get(i);
644 SharedElementOriginalState state = originalState.get(i);
645 if (view instanceof ImageView && state.mScaleType != null) {
646 ImageView imageView = (ImageView) view;
647 imageView.setScaleType(state.mScaleType);
648 if (state.mScaleType == ImageView.ScaleType.MATRIX) {
649 imageView.setImageMatrix(state.mMatrix);
650 }
651 }
George Mount26c82b62014-08-08 15:43:59 -0700652 view.setElevation(state.mElevation);
653 view.setTranslationZ(state.mTranslationZ);
654 int widthSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredWidth,
Dake Guc18f4cc2014-07-11 17:48:37 -0700655 View.MeasureSpec.EXACTLY);
656 int heightSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredHeight,
657 View.MeasureSpec.EXACTLY);
658 view.measure(widthSpec, heightSpec);
659 view.layout(state.mLeft, state.mTop, state.mRight, state.mBottom);
George Mountc93ca162014-05-23 19:21:36 -0700660 }
661 }
662
663 protected Bundle captureSharedElementState() {
664 Bundle bundle = new Bundle();
Dake Gu7bf379c2014-07-15 16:29:38 -0700665 RectF tempBounds = new RectF();
666 Matrix tempMatrix = new Matrix();
Dake Guf03c1392014-07-25 15:36:23 -0700667 for (int i = 0; i < mSharedElements.size(); i++) {
George Mountc93ca162014-05-23 19:21:36 -0700668 View sharedElement = mSharedElements.get(i);
669 String name = mSharedElementNames.get(i);
Dake Gu7bf379c2014-07-15 16:29:38 -0700670 captureSharedElementState(sharedElement, name, bundle, tempMatrix, tempBounds);
George Mountc93ca162014-05-23 19:21:36 -0700671 }
672 return bundle;
673 }
674
George Mounta0a02602014-06-20 18:22:26 -0700675 protected void clearState() {
676 // Clear the state so that we can't hold any references accidentally and leak memory.
677 mWindow = null;
George Mounta0a02602014-06-20 18:22:26 -0700678 mSharedElements.clear();
George Mountb694e082014-09-12 07:34:52 -0700679 mTransitioningViews = null;
George Mount0f0c4732014-09-05 13:47:47 -0700680 mOriginalAlphas.clear();
George Mounta0a02602014-06-20 18:22:26 -0700681 mResultReceiver = null;
682 mPendingTransition = null;
Dake Gufc0fc0e2014-08-01 15:39:11 -0700683 mListener = null;
George Mount333b8092014-10-21 15:09:11 -0700684 mSharedElementParentMatrices = null;
George Mounta0a02602014-06-20 18:22:26 -0700685 }
686
George Mounted1e01d2014-06-05 13:49:12 -0700687 protected long getFadeDuration() {
688 return getWindow().getTransitionBackgroundFadeDuration();
689 }
690
George Mount0f0c4732014-09-05 13:47:47 -0700691 protected void hideViews(ArrayList<View> views) {
George Mountfe361d22014-07-08 17:25:25 -0700692 int count = views.size();
693 for (int i = 0; i < count; i++) {
George Mount0f0c4732014-09-05 13:47:47 -0700694 View view = views.get(i);
695 if (!mOriginalAlphas.containsKey(view)) {
696 mOriginalAlphas.put(view, view.getAlpha());
697 }
698 view.setAlpha(0f);
George Mount0f0c4732014-09-05 13:47:47 -0700699 }
700 }
701
George Mountce2ee3d2014-09-09 15:04:31 -0700702 protected void showViews(ArrayList<View> views, boolean setTransitionAlpha) {
George Mount0f0c4732014-09-05 13:47:47 -0700703 int count = views.size();
704 for (int i = 0; i < count; i++) {
George Mount653ea662014-09-12 13:56:32 -0700705 showView(views.get(i), setTransitionAlpha);
706 }
707 }
708
709 private void showView(View view, boolean setTransitionAlpha) {
710 Float alpha = mOriginalAlphas.remove(view);
711 if (alpha != null) {
712 view.setAlpha(alpha);
713 }
714 if (setTransitionAlpha) {
715 view.setTransitionAlpha(1f);
George Mountb5ef7f82014-07-09 14:55:03 -0700716 }
717 }
718
George Mountc93ca162014-05-23 19:21:36 -0700719 /**
720 * Captures placement information for Views with a shared element name for
721 * Activity Transitions.
722 *
723 * @param view The View to capture the placement information for.
724 * @param name The shared element name in the target Activity to apply the placement
725 * information for.
726 * @param transitionArgs Bundle to store shared element placement information.
George Mount8e43d6d2014-06-05 17:25:46 -0700727 * @param tempBounds A temporary Rect for capturing the current location of views.
George Mountc93ca162014-05-23 19:21:36 -0700728 */
George Mount480ca822014-08-08 16:35:48 -0700729 protected void captureSharedElementState(View view, String name, Bundle transitionArgs,
Dake Gu7bf379c2014-07-15 16:29:38 -0700730 Matrix tempMatrix, RectF tempBounds) {
George Mountc93ca162014-05-23 19:21:36 -0700731 Bundle sharedElementBundle = new Bundle();
Dake Gu7bf379c2014-07-15 16:29:38 -0700732 tempMatrix.reset();
733 view.transformMatrixToGlobal(tempMatrix);
George Mount8e43d6d2014-06-05 17:25:46 -0700734 tempBounds.set(0, 0, view.getWidth(), view.getHeight());
Dake Gu7bf379c2014-07-15 16:29:38 -0700735 tempMatrix.mapRect(tempBounds);
George Mountc93ca162014-05-23 19:21:36 -0700736
Dake Gu7bf379c2014-07-15 16:29:38 -0700737 sharedElementBundle.putFloat(KEY_SCREEN_LEFT, tempBounds.left);
738 sharedElementBundle.putFloat(KEY_SCREEN_RIGHT, tempBounds.right);
739 sharedElementBundle.putFloat(KEY_SCREEN_TOP, tempBounds.top);
740 sharedElementBundle.putFloat(KEY_SCREEN_BOTTOM, tempBounds.bottom);
George Mountc93ca162014-05-23 19:21:36 -0700741 sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
George Mount26c82b62014-08-08 15:43:59 -0700742 sharedElementBundle.putFloat(KEY_ELEVATION, view.getElevation());
George Mountc93ca162014-05-23 19:21:36 -0700743
George Mount1732f522014-09-17 16:59:36 -0700744 Parcelable bitmap = null;
745 if (mListener != null) {
746 bitmap = mListener.onCaptureSharedElementSnapshot(view, tempMatrix, tempBounds);
747 }
748
George Mount480ca822014-08-08 16:35:48 -0700749 if (bitmap != null) {
750 sharedElementBundle.putParcelable(KEY_SNAPSHOT, bitmap);
George Mountc93ca162014-05-23 19:21:36 -0700751 }
752
753 if (view instanceof ImageView) {
754 ImageView imageView = (ImageView) view;
755 int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
756 sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
757 if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
758 float[] matrix = new float[9];
759 imageView.getImageMatrix().getValues(matrix);
760 sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
761 }
762 }
763
764 transitionArgs.putBundle(name, sharedElementBundle);
765 }
766
George Mount67d92432014-06-06 13:34:20 -0700767
768 protected void startTransition(Runnable runnable) {
769 if (mIsStartingTransition) {
770 mPendingTransition = runnable;
771 } else {
772 mIsStartingTransition = true;
773 runnable.run();
774 }
775 }
776
George Mount13ccb792014-06-06 17:02:20 -0700777 protected void transitionStarted() {
778 mIsStartingTransition = false;
779 }
780
George Mountfbd45962015-01-26 14:38:19 -0800781 /**
782 * Cancels any pending transitions and returns true if there is a transition is in
783 * the middle of starting.
784 */
785 protected boolean cancelPendingTransitions() {
786 mPendingTransition = null;
787 return mIsStartingTransition;
788 }
789
George Mountfe361d22014-07-08 17:25:25 -0700790 protected void moveSharedElementsToOverlay() {
George Mounteca1ae52014-10-27 14:25:12 -0700791 if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
George Mountb89d5cc2014-08-18 16:50:50 -0700792 return;
793 }
George Mount333b8092014-10-21 15:09:11 -0700794 setSharedElementMatrices();
George Mountfe361d22014-07-08 17:25:25 -0700795 int numSharedElements = mSharedElements.size();
796 ViewGroup decor = getDecor();
797 if (decor != null) {
798 boolean moveWithParent = moveSharedElementWithParent();
George Mount42161c52015-02-24 16:18:09 -0800799 Matrix tempMatrix = new Matrix();
George Mountfe361d22014-07-08 17:25:25 -0700800 for (int i = 0; i < numSharedElements; i++) {
801 View view = mSharedElements.get(i);
George Mount42161c52015-02-24 16:18:09 -0800802 tempMatrix.reset();
803 mSharedElementParentMatrices.get(i).invert(tempMatrix);
804 GhostView.addGhost(view, decor, tempMatrix);
George Mountfe361d22014-07-08 17:25:25 -0700805 ViewGroup parent = (ViewGroup) view.getParent();
806 if (moveWithParent && !isInTransitionGroup(parent, decor)) {
George Mount6e7fb602014-09-04 16:20:20 -0700807 GhostViewListeners listener = new GhostViewListeners(view, parent, decor);
George Mountfe361d22014-07-08 17:25:25 -0700808 parent.getViewTreeObserver().addOnPreDrawListener(listener);
809 mGhostViewListeners.add(listener);
810 }
811 }
812 }
813 }
814
815 protected boolean moveSharedElementWithParent() {
816 return true;
817 }
818
819 public static boolean isInTransitionGroup(ViewParent viewParent, ViewGroup decor) {
820 if (viewParent == decor || !(viewParent instanceof ViewGroup)) {
821 return false;
822 }
823 ViewGroup parent = (ViewGroup) viewParent;
824 if (parent.isTransitionGroup()) {
825 return true;
826 } else {
827 return isInTransitionGroup(parent.getParent(), decor);
828 }
829 }
830
831 protected void moveSharedElementsFromOverlay() {
George Mountb89d5cc2014-08-18 16:50:50 -0700832 int numListeners = mGhostViewListeners.size();
833 for (int i = 0; i < numListeners; i++) {
834 GhostViewListeners listener = mGhostViewListeners.get(i);
835 ViewGroup parent = (ViewGroup) listener.getView().getParent();
836 parent.getViewTreeObserver().removeOnPreDrawListener(listener);
837 }
838 mGhostViewListeners.clear();
839
840 if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
841 return;
842 }
George Mountfe361d22014-07-08 17:25:25 -0700843 ViewGroup decor = getDecor();
844 if (decor != null) {
845 ViewGroupOverlay overlay = decor.getOverlay();
846 int count = mSharedElements.size();
847 for (int i = 0; i < count; i++) {
848 View sharedElement = mSharedElements.get(i);
849 GhostView.removeGhost(sharedElement);
850 }
851 }
George Mountfe361d22014-07-08 17:25:25 -0700852 }
853
854 protected void setGhostVisibility(int visibility) {
855 int numSharedElements = mSharedElements.size();
856 for (int i = 0; i < numSharedElements; i++) {
857 GhostView ghostView = GhostView.getGhost(mSharedElements.get(i));
858 if (ghostView != null) {
859 ghostView.setVisibility(visibility);
860 }
861 }
862 }
863
864 protected void scheduleGhostVisibilityChange(final int visibility) {
George Mount6e7fb602014-09-04 16:20:20 -0700865 final View decorView = getDecor();
George Mount48bd13c2014-09-12 10:54:54 -0700866 if (decorView != null) {
867 decorView.getViewTreeObserver()
868 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
869 @Override
870 public boolean onPreDraw() {
871 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
872 setGhostVisibility(visibility);
873 return true;
874 }
875 });
876 }
George Mountfe361d22014-07-08 17:25:25 -0700877 }
878
George Mount41725de2015-04-09 08:23:05 -0700879 protected boolean isViewsTransitionComplete() {
880 return mViewsTransitionComplete;
881 }
882
883 protected void viewsTransitionComplete() {
884 mViewsTransitionComplete = true;
885 startInputWhenTransitionsComplete();
886 }
887
888 protected void sharedElementTransitionComplete() {
889 mSharedElementTransitionComplete = true;
890 startInputWhenTransitionsComplete();
891 }
892 private void startInputWhenTransitionsComplete() {
893 if (mViewsTransitionComplete && mSharedElementTransitionComplete) {
894 final View decor = getDecor();
895 if (decor != null) {
896 final ViewRootImpl viewRoot = decor.getViewRootImpl();
George Mount5fddd4c2016-01-14 08:24:20 -0800897 if (viewRoot != null) {
898 viewRoot.setPausedForTransition(false);
899 }
George Mount41725de2015-04-09 08:23:05 -0700900 }
901 onTransitionsComplete();
902 }
903 }
904
George Mount80141d12015-07-14 10:03:06 -0700905 protected void pauseInput() {
906 final View decor = getDecor();
907 final ViewRootImpl viewRoot = decor == null ? null : decor.getViewRootImpl();
908 if (viewRoot != null) {
909 viewRoot.setPausedForTransition(true);
910 }
911 }
912
George Mount41725de2015-04-09 08:23:05 -0700913 protected void onTransitionsComplete() {}
914
George Mount67d92432014-06-06 13:34:20 -0700915 protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter {
916 @Override
917 public void onTransitionStart(Transition transition) {
918 mIsStartingTransition = false;
919 Runnable pending = mPendingTransition;
920 mPendingTransition = null;
921 if (pending != null) {
922 startTransition(pending);
923 }
924 }
925 }
926
George Mountc93ca162014-05-23 19:21:36 -0700927 private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
928 for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
929 if (scaleType == SCALE_TYPE_VALUES[i]) {
930 return i;
931 }
932 }
933 return -1;
934 }
935
George Mount62976722016-02-04 16:45:53 -0800936 protected void setTransitioningViewsVisiblity(int visiblity, boolean invalidate) {
937 final int numElements = mTransitioningViews == null ? 0 : mTransitioningViews.size();
938 for (int i = 0; i < numElements; i++) {
939 final View view = mTransitioningViews.get(i);
940 view.setTransitionVisibility(visiblity);
941 if (invalidate) {
942 view.invalidate();
943 }
944 }
945 }
946
George Mount31a21722014-03-24 17:44:36 -0700947 private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
948 private Rect mEpicenter;
949
950 public void setEpicenter(Rect epicenter) { mEpicenter = epicenter; }
951
952 @Override
George Mountdc21d3b2014-06-05 09:42:48 -0700953 public Rect onGetEpicenter(Transition transition) {
George Mount31a21722014-03-24 17:44:36 -0700954 return mEpicenter;
955 }
956 }
George Mount800d72b2014-05-19 07:09:00 -0700957
George Mountfe361d22014-07-08 17:25:25 -0700958 private static class GhostViewListeners implements ViewTreeObserver.OnPreDrawListener {
959 private View mView;
960 private ViewGroup mDecor;
George Mount6e7fb602014-09-04 16:20:20 -0700961 private View mParent;
George Mountfe361d22014-07-08 17:25:25 -0700962 private Matrix mMatrix = new Matrix();
963
George Mount6e7fb602014-09-04 16:20:20 -0700964 public GhostViewListeners(View view, View parent, ViewGroup decor) {
George Mountfe361d22014-07-08 17:25:25 -0700965 mView = view;
George Mount6e7fb602014-09-04 16:20:20 -0700966 mParent = parent;
George Mountfe361d22014-07-08 17:25:25 -0700967 mDecor = decor;
968 }
969
970 public View getView() {
971 return mView;
972 }
973
974 @Override
975 public boolean onPreDraw() {
George Mountfe361d22014-07-08 17:25:25 -0700976 GhostView ghostView = GhostView.getGhost(mView);
977 if (ghostView == null) {
George Mount6e7fb602014-09-04 16:20:20 -0700978 mParent.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountfe361d22014-07-08 17:25:25 -0700979 } else {
980 GhostView.calculateMatrix(mView, mDecor, mMatrix);
981 ghostView.setMatrix(mMatrix);
982 }
983 return true;
984 }
985 }
986
Dake Guc18f4cc2014-07-11 17:48:37 -0700987 static class SharedElementOriginalState {
988 int mLeft;
989 int mTop;
990 int mRight;
991 int mBottom;
992 int mMeasuredWidth;
993 int mMeasuredHeight;
994 ImageView.ScaleType mScaleType;
995 Matrix mMatrix;
George Mount26c82b62014-08-08 15:43:59 -0700996 float mTranslationZ;
997 float mElevation;
Dake Guc18f4cc2014-07-11 17:48:37 -0700998 }
George Mount31a21722014-03-24 17:44:36 -0700999}