blob: 9e80a4b0ec1bf237fd4cb534b482f0cee0ed7c86 [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 Mountc93ca162014-05-23 19:21:36 -070034import android.view.ViewTreeObserver;
George Mount31a21722014-03-24 17:44:36 -070035import android.view.Window;
George Mountcaa03102014-04-15 09:01:32 -070036import android.widget.ImageView;
George Mount31a21722014-03-24 17:44:36 -070037
38import java.util.ArrayList;
39import java.util.Collection;
40
41/**
42 * Base class for ExitTransitionCoordinator and EnterTransitionCoordinator, classes
43 * that manage activity transitions and the communications coordinating them between
44 * Activities. The ExitTransitionCoordinator is created in the
45 * ActivityOptions#makeSceneTransitionAnimation. The EnterTransitionCoordinator
46 * is created by ActivityOptions#createEnterActivityTransition by Activity when the window is
47 * attached.
48 *
49 * Typical startActivity goes like this:
50 * 1) ExitTransitionCoordinator created with ActivityOptions#makeSceneTransitionAnimation
51 * 2) Activity#startActivity called and that calls startExit() through
52 * ActivityOptions#dispatchStartExit
53 * - Exit transition starts by setting transitioning Views to INVISIBLE
54 * 3) Launched Activity starts, creating an EnterTransitionCoordinator.
55 * - The Window is made translucent
56 * - The Window background alpha is set to 0
57 * - The transitioning views are made INVISIBLE
58 * - MSG_SET_LISTENER is sent back to the ExitTransitionCoordinator.
59 * 4) The shared element transition completes.
60 * - MSG_TAKE_SHARED_ELEMENTS is sent to the EnterTransitionCoordinator
61 * 5) The MSG_TAKE_SHARED_ELEMENTS is received by the EnterTransitionCoordinator.
62 * - Shared elements are made VISIBLE
63 * - Shared elements positions and size are set to match the end state of the calling
64 * Activity.
65 * - The shared element transition is started
66 * - If the window allows overlapping transitions, the views transition is started by setting
67 * the entering Views to VISIBLE and the background alpha is animated to opaque.
68 * - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
69 * 6) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
70 * - The shared elements are made INVISIBLE
71 * 7) The exit transition completes in the calling Activity.
72 * - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
73 * 8) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
74 * - If the window doesn't allow overlapping enter transitions, the enter transition is started
75 * by setting entering views to VISIBLE and the background is animated to opaque.
76 * 9) The background opacity animation completes.
77 * - The window is made opaque
78 * 10) The calling Activity gets an onStop() call
79 * - onActivityStopped() is called and all exited Views are made VISIBLE.
80 *
Craig Mautner73f843d2014-05-19 09:42:28 -070081 * Typical finishAfterTransition goes like this:
82 * 1) finishAfterTransition() creates an ExitTransitionCoordinator and calls startExit()
George Mount62ab9b72014-05-02 13:51:17 -070083 * - The Window start transitioning to Translucent with a new ActivityOptions.
George Mount31a21722014-03-24 17:44:36 -070084 * - If no background exists, a black background is substituted
George Mount31a21722014-03-24 17:44:36 -070085 * - The shared elements in the scene are matched against those shared elements
86 * that were sent by comparing the names.
87 * - The exit transition is started by setting Views to INVISIBLE.
George Mount62ab9b72014-05-02 13:51:17 -070088 * 2) The ActivityOptions is received by the Activity and an EnterTransitionCoordinator is created.
George Mount31a21722014-03-24 17:44:36 -070089 * - All transitioning views are made VISIBLE to reverse what was done when onActivityStopped()
90 * was called
91 * 3) The Window is made translucent and a callback is received
92 * - The background alpha is animated to 0
93 * 4) The background alpha animation completes
94 * 5) The shared element transition completes
95 * - After both 4 & 5 complete, MSG_TAKE_SHARED_ELEMENTS is sent to the
George Mount62ab9b72014-05-02 13:51:17 -070096 * EnterTransitionCoordinator
97 * 6) MSG_TAKE_SHARED_ELEMENTS is received by EnterTransitionCoordinator
George Mount31a21722014-03-24 17:44:36 -070098 * - Shared elements are made VISIBLE
99 * - Shared elements positions and size are set to match the end state of the calling
100 * Activity.
101 * - The shared element transition is started
102 * - If the window allows overlapping transitions, the views transition is started by setting
103 * the entering Views to VISIBLE.
George Mount62ab9b72014-05-02 13:51:17 -0700104 * - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
105 * 7) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
George Mount31a21722014-03-24 17:44:36 -0700106 * - The shared elements are made INVISIBLE
107 * 8) The exit transition completes in the finishing Activity.
George Mount62ab9b72014-05-02 13:51:17 -0700108 * - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
George Mount31a21722014-03-24 17:44:36 -0700109 * - finish() is called on the exiting Activity
George Mount62ab9b72014-05-02 13:51:17 -0700110 * 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
George Mount31a21722014-03-24 17:44:36 -0700111 * - If the window doesn't allow overlapping enter transitions, the enter transition is started
112 * by setting entering views to VISIBLE.
113 */
114abstract class ActivityTransitionCoordinator extends ResultReceiver {
115 private static final String TAG = "ActivityTransitionCoordinator";
116
117 /**
George Mount31a21722014-03-24 17:44:36 -0700118 * For Activity transitions, the called Activity's listener to receive calls
119 * when transitions complete.
120 */
George Mount62ab9b72014-05-02 13:51:17 -0700121 static final String KEY_REMOTE_RECEIVER = "android:remoteReceiver";
George Mount31a21722014-03-24 17:44:36 -0700122
Dake Gu7bf379c2014-07-15 16:29:38 -0700123 protected static final String KEY_SCREEN_LEFT = "shared_element:screenLeft";
124 protected static final String KEY_SCREEN_TOP = "shared_element:screenTop";
125 protected static final String KEY_SCREEN_RIGHT = "shared_element:screenRight";
126 protected static final String KEY_SCREEN_BOTTOM= "shared_element:screenBottom";
George Mount62ab9b72014-05-02 13:51:17 -0700127 protected static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
George Mount480ca822014-08-08 16:35:48 -0700128 protected static final String KEY_SNAPSHOT = "shared_element:bitmap";
George Mount62ab9b72014-05-02 13:51:17 -0700129 protected static final String KEY_SCALE_TYPE = "shared_element:scaleType";
130 protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
George Mount26c82b62014-08-08 15:43:59 -0700131 protected static final String KEY_ELEVATION = "shared_element:elevation";
George Mount080443b2014-05-05 10:47:00 -0700132
George Mount62ab9b72014-05-02 13:51:17 -0700133 protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
George Mount31a21722014-03-24 17:44:36 -0700134
135 /**
136 * Sent by the exiting coordinator (either EnterTransitionCoordinator
137 * or ExitTransitionCoordinator) after the shared elements have
138 * become stationary (shared element transition completes). This tells
139 * the remote coordinator to take control of the shared elements and
140 * that animations may begin. The remote Activity won't start entering
141 * until this message is received, but may wait for
142 * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
143 */
George Mount62ab9b72014-05-02 13:51:17 -0700144 public static final int MSG_SET_REMOTE_RECEIVER = 100;
George Mount31a21722014-03-24 17:44:36 -0700145
146 /**
147 * Sent by the entering coordinator to tell the exiting coordinator
148 * to hide its shared elements after it has started its shared
149 * element transition. This is temporary until the
150 * interlock of shared elements is figured out.
151 */
152 public static final int MSG_HIDE_SHARED_ELEMENTS = 101;
153
154 /**
George Mount31a21722014-03-24 17:44:36 -0700155 * Sent by the exiting coordinator (either EnterTransitionCoordinator
156 * or ExitTransitionCoordinator) after the shared elements have
157 * become stationary (shared element transition completes). This tells
158 * the remote coordinator to take control of the shared elements and
159 * that animations may begin. The remote Activity won't start entering
160 * until this message is received, but may wait for
161 * MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
162 */
George Mount62ab9b72014-05-02 13:51:17 -0700163 public static final int MSG_TAKE_SHARED_ELEMENTS = 103;
George Mount31a21722014-03-24 17:44:36 -0700164
165 /**
166 * Sent by the exiting coordinator (either
167 * EnterTransitionCoordinator or ExitTransitionCoordinator) after
168 * the exiting Views have finished leaving the scene. This will
169 * be ignored if allowOverlappingTransitions() is true on the
170 * remote coordinator. If it is false, it will trigger the enter
171 * transition to start.
172 */
George Mount62ab9b72014-05-02 13:51:17 -0700173 public static final int MSG_EXIT_TRANSITION_COMPLETE = 104;
George Mount31a21722014-03-24 17:44:36 -0700174
175 /**
176 * Sent by Activity#startActivity to begin the exit transition.
177 */
George Mount62ab9b72014-05-02 13:51:17 -0700178 public static final int MSG_START_EXIT_TRANSITION = 105;
George Mount31a21722014-03-24 17:44:36 -0700179
George Mount62ab9b72014-05-02 13:51:17 -0700180 /**
181 * It took too long for a message from the entering Activity, so we canceled the transition.
182 */
183 public static final int MSG_CANCEL = 106;
George Mount31a21722014-03-24 17:44:36 -0700184
George Mountc93ca162014-05-23 19:21:36 -0700185 /**
186 * When returning, this is the destination location for the shared element.
187 */
188 public static final int MSG_SHARED_ELEMENT_DESTINATION = 107;
189
George Mountd265bd72014-06-02 07:22:59 -0700190 /**
191 * Send the shared element positions.
192 */
193 public static final int MSG_SEND_SHARED_ELEMENT_DESTINATION = 108;
194
George Mounta0a02602014-06-20 18:22:26 -0700195 private Window mWindow;
George Mount62ab9b72014-05-02 13:51:17 -0700196 final protected ArrayList<String> mAllSharedElementNames;
197 final protected ArrayList<View> mSharedElements = new ArrayList<View>();
198 final protected ArrayList<String> mSharedElementNames = new ArrayList<String>();
199 final protected ArrayList<View> mTransitioningViews = new ArrayList<View>();
George Mount65580562014-08-29 08:15:48 -0700200 protected SharedElementCallback mListener;
George Mount62ab9b72014-05-02 13:51:17 -0700201 protected ResultReceiver mResultReceiver;
202 final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
George Mountc9b6df82014-05-21 15:07:04 -0700203 final protected boolean mIsReturning;
George Mount67d92432014-06-06 13:34:20 -0700204 private Runnable mPendingTransition;
205 private boolean mIsStartingTransition;
George Mountfe361d22014-07-08 17:25:25 -0700206 private ArrayList<GhostViewListeners> mGhostViewListeners =
207 new ArrayList<GhostViewListeners>();
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 Mount65580562014-08-29 08:15:48 -0700221 mListener.onMapSharedElements(mAllSharedElementNames, sharedElements);
George Mountce725a42014-08-27 16:10:46 -0700222 mSharedElementNames.addAll(sharedElements.keySet());
223 mSharedElements.addAll(sharedElements.values());
George Mount31a21722014-03-24 17:44:36 -0700224 if (getViewsTransition() != null) {
George Mount62ab9b72014-05-02 13:51:17 -0700225 getDecor().captureTransitioningViews(mTransitioningViews);
226 mTransitioningViews.removeAll(mSharedElements);
George Mount31a21722014-03-24 17:44:36 -0700227 }
George Mount62ab9b72014-05-02 13:51:17 -0700228 setEpicenter();
George Mount31a21722014-03-24 17:44:36 -0700229 }
230
George Mount60625b02014-06-24 07:46:23 -0700231 protected void stripOffscreenViews() {
232 Rect r = new Rect();
233 for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
234 View view = mTransitioningViews.get(i);
235 if (!view.getGlobalVisibleRect(r)) {
236 mTransitioningViews.remove(i);
237 }
238 }
239 }
240
George Mount31a21722014-03-24 17:44:36 -0700241 protected Window getWindow() {
242 return mWindow;
243 }
244
George Mounta2bbbb32014-08-12 10:16:20 -0700245 public ViewGroup getDecor() {
George Mount31a21722014-03-24 17:44:36 -0700246 return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView();
247 }
248
George Mount62ab9b72014-05-02 13:51:17 -0700249 /**
250 * Sets the transition epicenter to the position of the first shared element.
251 */
252 protected void setEpicenter() {
253 View epicenter = null;
George Mountc6186bf2014-09-04 16:33:59 -0700254 if (!mAllSharedElementNames.isEmpty() && !mSharedElementNames.isEmpty()) {
255 int index = mSharedElementNames.indexOf(mAllSharedElementNames.get(0));
256 if (index >= 0) {
257 epicenter = mSharedElements.get(index);
258 }
George Mount31a21722014-03-24 17:44:36 -0700259 }
George Mount62ab9b72014-05-02 13:51:17 -0700260 setEpicenter(epicenter);
George Mount31a21722014-03-24 17:44:36 -0700261 }
262
George Mount62ab9b72014-05-02 13:51:17 -0700263 private void setEpicenter(View view) {
264 if (view == null) {
265 mEpicenterCallback.setEpicenter(null);
George Mount31a21722014-03-24 17:44:36 -0700266 } else {
George Mount8e43d6d2014-06-05 17:25:46 -0700267 Rect epicenter = new Rect();
268 view.getBoundsOnScreen(epicenter);
George Mount62ab9b72014-05-02 13:51:17 -0700269 mEpicenterCallback.setEpicenter(epicenter);
George Mount31a21722014-03-24 17:44:36 -0700270 }
271 }
272
George Mount62ab9b72014-05-02 13:51:17 -0700273 public ArrayList<String> getAcceptedNames() {
274 return mSharedElementNames;
George Mount31a21722014-03-24 17:44:36 -0700275 }
276
George Mount62ab9b72014-05-02 13:51:17 -0700277 public ArrayList<String> getMappedNames() {
278 ArrayList<String> names = new ArrayList<String>(mSharedElements.size());
279 for (int i = 0; i < mSharedElements.size(); i++) {
George Mount0a2ae002014-06-23 14:57:27 +0000280 names.add(mSharedElements.get(i).getTransitionName());
George Mount31a21722014-03-24 17:44:36 -0700281 }
George Mount62ab9b72014-05-02 13:51:17 -0700282 return names;
George Mount31a21722014-03-24 17:44:36 -0700283 }
284
George Mount700db2a2014-07-07 17:17:49 -0700285 public ArrayList<View> copyMappedViews() {
286 return new ArrayList<View>(mSharedElements);
George Mount1fecfb22014-06-18 14:55:55 -0700287 }
288
George Mount8c2614c2014-06-10 11:12:01 -0700289 public ArrayList<String> getAllSharedElementNames() { return mAllSharedElementNames; }
290
George Mount88815022014-06-25 14:33:54 -0700291 protected Transition setTargets(Transition transition, boolean add) {
292 if (transition == null || (add &&
293 (mTransitioningViews == null || mTransitioningViews.isEmpty()))) {
George Mount31a21722014-03-24 17:44:36 -0700294 return null;
295 }
George Mountd4c3c912014-06-09 12:31:34 -0700296 // Add the targets to a set containing transition so that transition
297 // remains unaffected. We don't want to modify the targets of transition itself.
George Mount31a21722014-03-24 17:44:36 -0700298 TransitionSet set = new TransitionSet();
George Mount88815022014-06-25 14:33:54 -0700299 if (mTransitioningViews != null) {
300 for (int i = mTransitioningViews.size() - 1; i >= 0; i--) {
301 View view = mTransitioningViews.get(i);
302 if (add) {
303 set.addTarget(view);
304 } else {
305 set.excludeTarget(view, true);
306 }
George Mount31a21722014-03-24 17:44:36 -0700307 }
308 }
George Mountd4c3c912014-06-09 12:31:34 -0700309 // By adding the transition after addTarget, we prevent addTarget from
310 // affecting transition.
311 set.addTransition(transition);
George Mountfe361d22014-07-08 17:25:25 -0700312
313 if (!add && mTransitioningViews != null && !mTransitioningViews.isEmpty()) {
314 // Allow children of excluded transitioning views, but not the views themselves
315 set = new TransitionSet().addTransition(set);
316 }
317
George Mount31a21722014-03-24 17:44:36 -0700318 return set;
319 }
320
George Mount88815022014-06-25 14:33:54 -0700321 protected Transition configureTransition(Transition transition,
322 boolean includeTransitioningViews) {
George Mount31a21722014-03-24 17:44:36 -0700323 if (transition != null) {
George Mount62ab9b72014-05-02 13:51:17 -0700324 transition = transition.clone();
325 transition.setEpicenterCallback(mEpicenterCallback);
George Mount88815022014-06-25 14:33:54 -0700326 transition = setTargets(transition, includeTransitioningViews);
George Mount31a21722014-03-24 17:44:36 -0700327 }
328 return transition;
329 }
330
George Mount62ab9b72014-05-02 13:51:17 -0700331 protected static Transition mergeTransitions(Transition transition1, Transition transition2) {
332 if (transition1 == null) {
333 return transition2;
334 } else if (transition2 == null) {
335 return transition1;
336 } else {
337 TransitionSet transitionSet = new TransitionSet();
338 transitionSet.addTransition(transition1);
339 transitionSet.addTransition(transition2);
340 return transitionSet;
George Mountcaa03102014-04-15 09:01:32 -0700341 }
George Mountcaa03102014-04-15 09:01:32 -0700342 }
343
George Mount1fecfb22014-06-18 14:55:55 -0700344 protected ArrayMap<String, View> mapSharedElements(ArrayList<String> accepted,
345 ArrayList<View> localViews) {
346 ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
George Mountce725a42014-08-27 16:10:46 -0700347 if (accepted != null) {
348 for (int i = 0; i < accepted.size(); i++) {
349 sharedElements.put(accepted.get(i), localViews.get(i));
George Mount62ab9b72014-05-02 13:51:17 -0700350 }
George Mountce725a42014-08-27 16:10:46 -0700351 } else {
352 getDecor().findNamedViews(sharedElements);
George Mount1fecfb22014-06-18 14:55:55 -0700353 }
354 return sharedElements;
355 }
356
George Mount62ab9b72014-05-02 13:51:17 -0700357 protected void setResultReceiver(ResultReceiver resultReceiver) {
358 mResultReceiver = resultReceiver;
George Mount080443b2014-05-05 10:47:00 -0700359 }
360
George Mounta712e8c2014-05-20 15:10:20 -0700361 protected abstract Transition getViewsTransition();
George Mount080443b2014-05-05 10:47:00 -0700362
George Mountfe361d22014-07-08 17:25:25 -0700363 private void setSharedElementState(View view, String name, Bundle transitionArgs,
Dake Gu7bf379c2014-07-15 16:29:38 -0700364 Matrix tempMatrix, RectF tempRect, int[] decorLoc) {
George Mountc93ca162014-05-23 19:21:36 -0700365 Bundle sharedElementBundle = transitionArgs.getBundle(name);
366 if (sharedElementBundle == null) {
367 return;
368 }
369
370 if (view instanceof ImageView) {
371 int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
372 if (scaleTypeInt >= 0) {
373 ImageView imageView = (ImageView) view;
374 ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
375 imageView.setScaleType(scaleType);
376 if (scaleType == ImageView.ScaleType.MATRIX) {
377 float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
Dake Gu7bf379c2014-07-15 16:29:38 -0700378 tempMatrix.setValues(matrixValues);
379 imageView.setImageMatrix(tempMatrix);
George Mountc93ca162014-05-23 19:21:36 -0700380 }
381 }
382 }
383
384 float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
385 view.setTranslationZ(z);
George Mount26c82b62014-08-08 15:43:59 -0700386 float elevation = sharedElementBundle.getFloat(KEY_ELEVATION);
387 view.setElevation(elevation);
George Mountc93ca162014-05-23 19:21:36 -0700388
Dake Gu7bf379c2014-07-15 16:29:38 -0700389 float left = sharedElementBundle.getFloat(KEY_SCREEN_LEFT);
390 float top = sharedElementBundle.getFloat(KEY_SCREEN_TOP);
391 float right = sharedElementBundle.getFloat(KEY_SCREEN_RIGHT);
392 float bottom = sharedElementBundle.getFloat(KEY_SCREEN_BOTTOM);
George Mountc93ca162014-05-23 19:21:36 -0700393
Dake Gu7bf379c2014-07-15 16:29:38 -0700394 if (decorLoc != null) {
395 left -= decorLoc[0];
396 top -= decorLoc[1];
George Mount480ca822014-08-08 16:35:48 -0700397 right -= decorLoc[0];
398 bottom -= decorLoc[1];
Dake Gu7bf379c2014-07-15 16:29:38 -0700399 } else {
400 // Find the location in the view's parent
George Mountfe361d22014-07-08 17:25:25 -0700401 getSharedElementParentMatrix(view, tempMatrix);
Dake Gu7bf379c2014-07-15 16:29:38 -0700402 tempRect.set(left, top, right, bottom);
403 tempMatrix.mapRect(tempRect);
404
405 float leftInParent = tempRect.left;
406 float topInParent = tempRect.top;
407
408 // Find the size of the view
409 view.getInverseMatrix().mapRect(tempRect);
410 float width = tempRect.width();
411 float height = tempRect.height();
412
413 // Now determine the offset due to view transform:
414 view.setLeft(0);
415 view.setTop(0);
416 view.setRight(Math.round(width));
417 view.setBottom(Math.round(height));
418 tempRect.set(0, 0, width, height);
419 view.getMatrix().mapRect(tempRect);
420
George Mountfe361d22014-07-08 17:25:25 -0700421 ViewGroup parent = (ViewGroup) view.getParent();
Dake Gu7bf379c2014-07-15 16:29:38 -0700422 left = leftInParent - tempRect.left + parent.getScrollX();
423 top = topInParent - tempRect.top + parent.getScrollY();
424 right = left + width;
425 bottom = top + height;
426 }
427
428 int x = Math.round(left);
429 int y = Math.round(top);
430 int width = Math.round(right) - x;
431 int height = Math.round(bottom) - y;
George Mountc93ca162014-05-23 19:21:36 -0700432 int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
433 int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
434 view.measure(widthSpec, heightSpec);
435
Dake Gu7bf379c2014-07-15 16:29:38 -0700436 view.layout(x, y, x + width, y + height);
George Mountc93ca162014-05-23 19:21:36 -0700437 }
438
George Mountfe361d22014-07-08 17:25:25 -0700439 protected void getSharedElementParentMatrix(View view, Matrix matrix) {
440 // Find the location in the view's parent
441 ViewGroup parent = (ViewGroup) view.getParent();
442 matrix.reset();
443 parent.transformMatrixToLocal(matrix);
444 }
445
Dake Guc18f4cc2014-07-11 17:48:37 -0700446 protected ArrayList<SharedElementOriginalState> setSharedElementState(
George Mountc93ca162014-05-23 19:21:36 -0700447 Bundle sharedElementState, final ArrayList<View> snapshots) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700448 ArrayList<SharedElementOriginalState> originalImageState =
449 new ArrayList<SharedElementOriginalState>();
George Mountc93ca162014-05-23 19:21:36 -0700450 if (sharedElementState != null) {
Dake Gu7bf379c2014-07-15 16:29:38 -0700451 Matrix tempMatrix = new Matrix();
452 RectF tempRect = new RectF();
George Mountc93ca162014-05-23 19:21:36 -0700453 for (int i = 0; i < mSharedElementNames.size(); i++) {
454 View sharedElement = mSharedElements.get(i);
455 String name = mSharedElementNames.get(i);
Dake Guc18f4cc2014-07-11 17:48:37 -0700456 SharedElementOriginalState originalState = getOldSharedElementState(sharedElement,
George Mountc93ca162014-05-23 19:21:36 -0700457 name, sharedElementState);
Dake Guc18f4cc2014-07-11 17:48:37 -0700458 originalImageState.add(originalState);
Dake Gu7bf379c2014-07-15 16:29:38 -0700459 setSharedElementState(sharedElement, name, sharedElementState,
460 tempMatrix, tempRect, null);
George Mountc93ca162014-05-23 19:21:36 -0700461 }
462 }
George Mount65580562014-08-29 08:15:48 -0700463 mListener.onSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
George Mountfe361d22014-07-08 17:25:25 -0700464 return originalImageState;
465 }
George Mountc93ca162014-05-23 19:21:36 -0700466
George Mountfe361d22014-07-08 17:25:25 -0700467 protected void notifySharedElementEnd(ArrayList<View> snapshots) {
George Mount65580562014-08-29 08:15:48 -0700468 mListener.onSharedElementEnd(mSharedElementNames, mSharedElements, snapshots);
George Mountfe361d22014-07-08 17:25:25 -0700469 }
470
471 protected void scheduleSetSharedElementEnd(final ArrayList<View> snapshots) {
George Mount6e7fb602014-09-04 16:20:20 -0700472 final View decorView = getDecor();
473 decorView.getViewTreeObserver().addOnPreDrawListener(
George Mountc93ca162014-05-23 19:21:36 -0700474 new ViewTreeObserver.OnPreDrawListener() {
475 @Override
476 public boolean onPreDraw() {
George Mount6e7fb602014-09-04 16:20:20 -0700477 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountfe361d22014-07-08 17:25:25 -0700478 notifySharedElementEnd(snapshots);
George Mountc93ca162014-05-23 19:21:36 -0700479 return true;
480 }
481 }
482 );
George Mountc93ca162014-05-23 19:21:36 -0700483 }
484
Dake Guc18f4cc2014-07-11 17:48:37 -0700485 private static SharedElementOriginalState getOldSharedElementState(View view, String name,
George Mountc93ca162014-05-23 19:21:36 -0700486 Bundle transitionArgs) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700487
488 SharedElementOriginalState state = new SharedElementOriginalState();
489 state.mLeft = view.getLeft();
490 state.mTop = view.getTop();
491 state.mRight = view.getRight();
492 state.mBottom = view.getBottom();
493 state.mMeasuredWidth = view.getMeasuredWidth();
494 state.mMeasuredHeight = view.getMeasuredHeight();
George Mount26c82b62014-08-08 15:43:59 -0700495 state.mTranslationZ = view.getTranslationZ();
496 state.mElevation = view.getElevation();
George Mountc93ca162014-05-23 19:21:36 -0700497 if (!(view instanceof ImageView)) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700498 return state;
George Mountc93ca162014-05-23 19:21:36 -0700499 }
500 Bundle bundle = transitionArgs.getBundle(name);
501 if (bundle == null) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700502 return state;
George Mountc93ca162014-05-23 19:21:36 -0700503 }
504 int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
505 if (scaleTypeInt < 0) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700506 return state;
George Mountc93ca162014-05-23 19:21:36 -0700507 }
508
509 ImageView imageView = (ImageView) view;
Dake Guc18f4cc2014-07-11 17:48:37 -0700510 state.mScaleType = imageView.getScaleType();
511 if (state.mScaleType == ImageView.ScaleType.MATRIX) {
512 state.mMatrix = new Matrix(imageView.getImageMatrix());
George Mountc93ca162014-05-23 19:21:36 -0700513 }
Dake Guc18f4cc2014-07-11 17:48:37 -0700514 return state;
George Mountc93ca162014-05-23 19:21:36 -0700515 }
516
517 protected ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
518 int numSharedElements = names.size();
519 if (numSharedElements == 0) {
520 return null;
521 }
522 ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
523 Context context = getWindow().getContext();
Dake Gu7bf379c2014-07-15 16:29:38 -0700524 int[] decorLoc = new int[2];
525 getDecor().getLocationOnScreen(decorLoc);
George Mountc93ca162014-05-23 19:21:36 -0700526 for (String name: names) {
527 Bundle sharedElementBundle = state.getBundle(name);
528 if (sharedElementBundle != null) {
George Mount480ca822014-08-08 16:35:48 -0700529 Parcelable parcelable = sharedElementBundle.getParcelable(KEY_SNAPSHOT);
530 View snapshot = null;
531 if (parcelable != null) {
George Mount65580562014-08-29 08:15:48 -0700532 snapshot = mListener.onCreateSnapshotView(context, parcelable);
George Mountc93ca162014-05-23 19:21:36 -0700533 }
George Mount480ca822014-08-08 16:35:48 -0700534 if (snapshot != null) {
535 setSharedElementState(snapshot, name, state, null, null, decorLoc);
536 }
George Mountc93ca162014-05-23 19:21:36 -0700537 snapshots.add(snapshot);
538 }
539 }
540 return snapshots;
541 }
542
Dake Guc18f4cc2014-07-11 17:48:37 -0700543 protected static void setOriginalSharedElementState(ArrayList<View> sharedElements,
544 ArrayList<SharedElementOriginalState> originalState) {
George Mountc93ca162014-05-23 19:21:36 -0700545 for (int i = 0; i < originalState.size(); i++) {
Dake Guc18f4cc2014-07-11 17:48:37 -0700546 View view = sharedElements.get(i);
547 SharedElementOriginalState state = originalState.get(i);
548 if (view instanceof ImageView && state.mScaleType != null) {
549 ImageView imageView = (ImageView) view;
550 imageView.setScaleType(state.mScaleType);
551 if (state.mScaleType == ImageView.ScaleType.MATRIX) {
552 imageView.setImageMatrix(state.mMatrix);
553 }
554 }
George Mount26c82b62014-08-08 15:43:59 -0700555 view.setElevation(state.mElevation);
556 view.setTranslationZ(state.mTranslationZ);
557 int widthSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredWidth,
Dake Guc18f4cc2014-07-11 17:48:37 -0700558 View.MeasureSpec.EXACTLY);
559 int heightSpec = View.MeasureSpec.makeMeasureSpec(state.mMeasuredHeight,
560 View.MeasureSpec.EXACTLY);
561 view.measure(widthSpec, heightSpec);
562 view.layout(state.mLeft, state.mTop, state.mRight, state.mBottom);
George Mountc93ca162014-05-23 19:21:36 -0700563 }
564 }
565
566 protected Bundle captureSharedElementState() {
567 Bundle bundle = new Bundle();
Dake Gu7bf379c2014-07-15 16:29:38 -0700568 RectF tempBounds = new RectF();
569 Matrix tempMatrix = new Matrix();
Dake Guf03c1392014-07-25 15:36:23 -0700570 for (int i = 0; i < mSharedElements.size(); i++) {
George Mountc93ca162014-05-23 19:21:36 -0700571 View sharedElement = mSharedElements.get(i);
572 String name = mSharedElementNames.get(i);
Dake Gu7bf379c2014-07-15 16:29:38 -0700573 captureSharedElementState(sharedElement, name, bundle, tempMatrix, tempBounds);
George Mountc93ca162014-05-23 19:21:36 -0700574 }
575 return bundle;
576 }
577
George Mounta0a02602014-06-20 18:22:26 -0700578 protected void clearState() {
579 // Clear the state so that we can't hold any references accidentally and leak memory.
580 mWindow = null;
George Mounta0a02602014-06-20 18:22:26 -0700581 mSharedElements.clear();
George Mounta0a02602014-06-20 18:22:26 -0700582 mTransitioningViews.clear();
583 mResultReceiver = null;
584 mPendingTransition = null;
Dake Gufc0fc0e2014-08-01 15:39:11 -0700585 mListener = null;
George Mounta0a02602014-06-20 18:22:26 -0700586 }
587
George Mounted1e01d2014-06-05 13:49:12 -0700588 protected long getFadeDuration() {
589 return getWindow().getTransitionBackgroundFadeDuration();
590 }
591
George Mountb5ef7f82014-07-09 14:55:03 -0700592 protected static void setTransitionAlpha(ArrayList<View> views, float alpha) {
George Mountfe361d22014-07-08 17:25:25 -0700593 int count = views.size();
594 for (int i = 0; i < count; i++) {
George Mountb5ef7f82014-07-09 14:55:03 -0700595 views.get(i).setTransitionAlpha(alpha);
596 }
597 }
598
George Mountc93ca162014-05-23 19:21:36 -0700599 /**
600 * Captures placement information for Views with a shared element name for
601 * Activity Transitions.
602 *
603 * @param view The View to capture the placement information for.
604 * @param name The shared element name in the target Activity to apply the placement
605 * information for.
606 * @param transitionArgs Bundle to store shared element placement information.
George Mount8e43d6d2014-06-05 17:25:46 -0700607 * @param tempBounds A temporary Rect for capturing the current location of views.
George Mountc93ca162014-05-23 19:21:36 -0700608 */
George Mount480ca822014-08-08 16:35:48 -0700609 protected void captureSharedElementState(View view, String name, Bundle transitionArgs,
Dake Gu7bf379c2014-07-15 16:29:38 -0700610 Matrix tempMatrix, RectF tempBounds) {
George Mountc93ca162014-05-23 19:21:36 -0700611 Bundle sharedElementBundle = new Bundle();
Dake Gu7bf379c2014-07-15 16:29:38 -0700612 tempMatrix.reset();
613 view.transformMatrixToGlobal(tempMatrix);
George Mount8e43d6d2014-06-05 17:25:46 -0700614 tempBounds.set(0, 0, view.getWidth(), view.getHeight());
Dake Gu7bf379c2014-07-15 16:29:38 -0700615 tempMatrix.mapRect(tempBounds);
George Mountc93ca162014-05-23 19:21:36 -0700616
Dake Gu7bf379c2014-07-15 16:29:38 -0700617 sharedElementBundle.putFloat(KEY_SCREEN_LEFT, tempBounds.left);
618 sharedElementBundle.putFloat(KEY_SCREEN_RIGHT, tempBounds.right);
619 sharedElementBundle.putFloat(KEY_SCREEN_TOP, tempBounds.top);
620 sharedElementBundle.putFloat(KEY_SCREEN_BOTTOM, tempBounds.bottom);
George Mountc93ca162014-05-23 19:21:36 -0700621 sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
George Mount26c82b62014-08-08 15:43:59 -0700622 sharedElementBundle.putFloat(KEY_ELEVATION, view.getElevation());
George Mountc93ca162014-05-23 19:21:36 -0700623
George Mount65580562014-08-29 08:15:48 -0700624 Parcelable bitmap = mListener.onCaptureSharedElementSnapshot(view, tempMatrix, tempBounds);
George Mount480ca822014-08-08 16:35:48 -0700625 if (bitmap != null) {
626 sharedElementBundle.putParcelable(KEY_SNAPSHOT, bitmap);
George Mountc93ca162014-05-23 19:21:36 -0700627 }
628
629 if (view instanceof ImageView) {
630 ImageView imageView = (ImageView) view;
631 int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
632 sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
633 if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
634 float[] matrix = new float[9];
635 imageView.getImageMatrix().getValues(matrix);
636 sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
637 }
638 }
639
640 transitionArgs.putBundle(name, sharedElementBundle);
641 }
642
George Mount67d92432014-06-06 13:34:20 -0700643
644 protected void startTransition(Runnable runnable) {
645 if (mIsStartingTransition) {
646 mPendingTransition = runnable;
647 } else {
648 mIsStartingTransition = true;
649 runnable.run();
650 }
651 }
652
George Mount13ccb792014-06-06 17:02:20 -0700653 protected void transitionStarted() {
654 mIsStartingTransition = false;
655 }
656
George Mountfe361d22014-07-08 17:25:25 -0700657 protected void moveSharedElementsToOverlay() {
George Mountb89d5cc2014-08-18 16:50:50 -0700658 if (!mWindow.getSharedElementsUseOverlay()) {
659 return;
660 }
George Mountfe361d22014-07-08 17:25:25 -0700661 int numSharedElements = mSharedElements.size();
662 ViewGroup decor = getDecor();
663 if (decor != null) {
664 boolean moveWithParent = moveSharedElementWithParent();
665 for (int i = 0; i < numSharedElements; i++) {
666 View view = mSharedElements.get(i);
667 GhostView.addGhost(view, decor);
668 ViewGroup parent = (ViewGroup) view.getParent();
669 if (moveWithParent && !isInTransitionGroup(parent, decor)) {
George Mount6e7fb602014-09-04 16:20:20 -0700670 GhostViewListeners listener = new GhostViewListeners(view, parent, decor);
George Mountfe361d22014-07-08 17:25:25 -0700671 parent.getViewTreeObserver().addOnPreDrawListener(listener);
672 mGhostViewListeners.add(listener);
673 }
674 }
675 }
676 }
677
678 protected boolean moveSharedElementWithParent() {
679 return true;
680 }
681
682 public static boolean isInTransitionGroup(ViewParent viewParent, ViewGroup decor) {
683 if (viewParent == decor || !(viewParent instanceof ViewGroup)) {
684 return false;
685 }
686 ViewGroup parent = (ViewGroup) viewParent;
687 if (parent.isTransitionGroup()) {
688 return true;
689 } else {
690 return isInTransitionGroup(parent.getParent(), decor);
691 }
692 }
693
694 protected void moveSharedElementsFromOverlay() {
George Mountb89d5cc2014-08-18 16:50:50 -0700695 int numListeners = mGhostViewListeners.size();
696 for (int i = 0; i < numListeners; i++) {
697 GhostViewListeners listener = mGhostViewListeners.get(i);
698 ViewGroup parent = (ViewGroup) listener.getView().getParent();
699 parent.getViewTreeObserver().removeOnPreDrawListener(listener);
700 }
701 mGhostViewListeners.clear();
702
703 if (mWindow == null || !mWindow.getSharedElementsUseOverlay()) {
704 return;
705 }
George Mountfe361d22014-07-08 17:25:25 -0700706 ViewGroup decor = getDecor();
707 if (decor != null) {
708 ViewGroupOverlay overlay = decor.getOverlay();
709 int count = mSharedElements.size();
710 for (int i = 0; i < count; i++) {
711 View sharedElement = mSharedElements.get(i);
712 GhostView.removeGhost(sharedElement);
713 }
714 }
George Mountfe361d22014-07-08 17:25:25 -0700715 }
716
717 protected void setGhostVisibility(int visibility) {
718 int numSharedElements = mSharedElements.size();
719 for (int i = 0; i < numSharedElements; i++) {
720 GhostView ghostView = GhostView.getGhost(mSharedElements.get(i));
721 if (ghostView != null) {
722 ghostView.setVisibility(visibility);
723 }
724 }
725 }
726
727 protected void scheduleGhostVisibilityChange(final int visibility) {
George Mount6e7fb602014-09-04 16:20:20 -0700728 final View decorView = getDecor();
729 decorView.getViewTreeObserver()
George Mountfe361d22014-07-08 17:25:25 -0700730 .addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
731 @Override
732 public boolean onPreDraw() {
George Mount6e7fb602014-09-04 16:20:20 -0700733 decorView.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountfe361d22014-07-08 17:25:25 -0700734 setGhostVisibility(visibility);
735 return true;
736 }
737 });
738 }
739
George Mount67d92432014-06-06 13:34:20 -0700740 protected class ContinueTransitionListener extends Transition.TransitionListenerAdapter {
741 @Override
742 public void onTransitionStart(Transition transition) {
743 mIsStartingTransition = false;
744 Runnable pending = mPendingTransition;
745 mPendingTransition = null;
746 if (pending != null) {
747 startTransition(pending);
748 }
749 }
750 }
751
George Mountc93ca162014-05-23 19:21:36 -0700752 private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
753 for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
754 if (scaleType == SCALE_TYPE_VALUES[i]) {
755 return i;
756 }
757 }
758 return -1;
759 }
760
George Mount31a21722014-03-24 17:44:36 -0700761 private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
762 private Rect mEpicenter;
763
764 public void setEpicenter(Rect epicenter) { mEpicenter = epicenter; }
765
766 @Override
George Mountdc21d3b2014-06-05 09:42:48 -0700767 public Rect onGetEpicenter(Transition transition) {
George Mount31a21722014-03-24 17:44:36 -0700768 return mEpicenter;
769 }
770 }
George Mount800d72b2014-05-19 07:09:00 -0700771
George Mountfe361d22014-07-08 17:25:25 -0700772 private static class GhostViewListeners implements ViewTreeObserver.OnPreDrawListener {
773 private View mView;
774 private ViewGroup mDecor;
George Mount6e7fb602014-09-04 16:20:20 -0700775 private View mParent;
George Mountfe361d22014-07-08 17:25:25 -0700776 private Matrix mMatrix = new Matrix();
777
George Mount6e7fb602014-09-04 16:20:20 -0700778 public GhostViewListeners(View view, View parent, ViewGroup decor) {
George Mountfe361d22014-07-08 17:25:25 -0700779 mView = view;
George Mount6e7fb602014-09-04 16:20:20 -0700780 mParent = parent;
George Mountfe361d22014-07-08 17:25:25 -0700781 mDecor = decor;
782 }
783
784 public View getView() {
785 return mView;
786 }
787
788 @Override
789 public boolean onPreDraw() {
George Mountfe361d22014-07-08 17:25:25 -0700790 GhostView ghostView = GhostView.getGhost(mView);
791 if (ghostView == null) {
George Mount6e7fb602014-09-04 16:20:20 -0700792 mParent.getViewTreeObserver().removeOnPreDrawListener(this);
George Mountfe361d22014-07-08 17:25:25 -0700793 } else {
794 GhostView.calculateMatrix(mView, mDecor, mMatrix);
795 ghostView.setMatrix(mMatrix);
796 }
797 return true;
798 }
799 }
800
Dake Guc18f4cc2014-07-11 17:48:37 -0700801 static class SharedElementOriginalState {
802 int mLeft;
803 int mTop;
804 int mRight;
805 int mBottom;
806 int mMeasuredWidth;
807 int mMeasuredHeight;
808 ImageView.ScaleType mScaleType;
809 Matrix mMatrix;
George Mount26c82b62014-08-08 15:43:59 -0700810 float mTranslationZ;
811 float mElevation;
Dake Guc18f4cc2014-07-11 17:48:37 -0700812 }
George Mount31a21722014-03-24 17:44:36 -0700813}