Merge "Support Activity Transitions when activity stopped."
This commit is contained in:
@@ -3257,6 +3257,7 @@ package android.app {
|
||||
method public boolean navigateUpToFromChild(android.app.Activity, android.content.Intent);
|
||||
method public void onActionModeFinished(android.view.ActionMode);
|
||||
method public void onActionModeStarted(android.view.ActionMode);
|
||||
method protected void onActivityReenter(int, android.content.Intent);
|
||||
method protected void onActivityResult(int, int, android.content.Intent);
|
||||
method public void onAttachFragment(android.app.Fragment);
|
||||
method public void onAttachedToWindow();
|
||||
@@ -3335,7 +3336,6 @@ package android.app {
|
||||
method public final boolean requestWindowFeature(int);
|
||||
method public final void runOnUiThread(java.lang.Runnable);
|
||||
method public void setActionBar(android.widget.Toolbar);
|
||||
method public void setActivityTransitionListener(android.app.ActivityOptions.ActivityTransitionListener);
|
||||
method public void setContentTransitionManager(android.transition.TransitionManager);
|
||||
method public void setContentView(int);
|
||||
method public void setContentView(android.view.View);
|
||||
@@ -3357,6 +3357,7 @@ package android.app {
|
||||
method public final void setResult(int);
|
||||
method public final void setResult(int, android.content.Intent);
|
||||
method public final void setSecondaryProgress(int);
|
||||
method public void setSharedElementListener(android.app.SharedElementListener);
|
||||
method public void setTitle(java.lang.CharSequence);
|
||||
method public void setTitle(int);
|
||||
method public deprecated void setTitleColor(int);
|
||||
@@ -3576,28 +3577,13 @@ package android.app {
|
||||
public class ActivityOptions {
|
||||
method public static android.app.ActivityOptions makeCustomAnimation(android.content.Context, int, int);
|
||||
method public static android.app.ActivityOptions makeScaleUpAnimation(android.view.View, int, int, int, int);
|
||||
method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.view.View, java.lang.String);
|
||||
method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.view.Window, android.app.ActivityOptions.ActivityTransitionListener);
|
||||
method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.view.View, java.lang.String);
|
||||
method public static android.app.ActivityOptions makeSceneTransitionAnimation(android.app.Activity, android.util.Pair<android.view.View, java.lang.String>...);
|
||||
method public static android.app.ActivityOptions makeThumbnailScaleUpAnimation(android.view.View, android.graphics.Bitmap, int, int);
|
||||
method public android.os.Bundle toBundle();
|
||||
method public void update(android.app.ActivityOptions);
|
||||
}
|
||||
|
||||
public static class ActivityOptions.ActivityTransitionListener {
|
||||
ctor public ActivityOptions.ActivityTransitionListener();
|
||||
method public android.util.Pair<android.view.View, java.lang.String>[] getSharedElementsMapping();
|
||||
method public boolean handleRejectedSharedElements(java.util.List<android.view.View>);
|
||||
method public void onCaptureSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
|
||||
method public void onCaptureSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
|
||||
method public void onEnterReady();
|
||||
method public void onExitTransitionComplete();
|
||||
method public void onRemoteExitComplete();
|
||||
method public void onSharedElementExitTransitionComplete();
|
||||
method public void onSharedElementTransferred(java.util.List<java.lang.String>, java.util.List<android.view.View>);
|
||||
method public void onStartEnterTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>);
|
||||
method public void onStartExitTransition(java.util.List<java.lang.String>, java.util.List<android.view.View>);
|
||||
}
|
||||
|
||||
public class AlarmManager {
|
||||
method public void cancel(android.app.PendingIntent);
|
||||
method public void set(int, long, android.app.PendingIntent);
|
||||
@@ -4821,6 +4807,14 @@ package android.app {
|
||||
field public static final int START_STICKY_COMPATIBILITY = 0; // 0x0
|
||||
}
|
||||
|
||||
public class SharedElementListener {
|
||||
ctor public SharedElementListener();
|
||||
method public void handleRejectedSharedElements(java.util.List<android.view.View>);
|
||||
method public void remapSharedElements(java.util.List<java.lang.String>, java.util.Map<java.lang.String, android.view.View>);
|
||||
method public void setSharedElementEnd(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
|
||||
method public void setSharedElementStart(java.util.List<java.lang.String>, java.util.List<android.view.View>, java.util.List<android.view.View>);
|
||||
}
|
||||
|
||||
public deprecated class TabActivity extends android.app.ActivityGroup {
|
||||
ctor public TabActivity();
|
||||
method public android.widget.TabHost getTabHost();
|
||||
|
||||
@@ -777,8 +777,9 @@ public class Activity extends ContextThemeWrapper
|
||||
|
||||
private Thread mUiThread;
|
||||
final Handler mHandler = new Handler();
|
||||
private ActivityOptions mCalledActivityOptions;
|
||||
private EnterTransitionCoordinator mEnterTransitionCoordinator;
|
||||
|
||||
private ActivityTransitionState mActivityTransitionState = new ActivityTransitionState();
|
||||
SharedElementListener mTransitionListener = new SharedElementListener();
|
||||
|
||||
/** Return the intent that started this activity. */
|
||||
public Intent getIntent() {
|
||||
@@ -1100,9 +1101,6 @@ public class Activity extends ContextThemeWrapper
|
||||
mTitleReady = true;
|
||||
onTitleChanged(getTitle(), getTitleColor());
|
||||
}
|
||||
if (mEnterTransitionCoordinator != null) {
|
||||
mEnterTransitionCoordinator.readyToEnter();
|
||||
}
|
||||
mCalled = true;
|
||||
}
|
||||
|
||||
@@ -1149,12 +1147,6 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
|
||||
getApplication().dispatchActivityStarted(this);
|
||||
|
||||
final ActivityOptions activityOptions = getActivityOptions();
|
||||
if (activityOptions != null &&
|
||||
activityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
|
||||
mEnterTransitionCoordinator = activityOptions.createEnterActivityTransition(this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1204,7 +1196,6 @@ public class Activity extends ContextThemeWrapper
|
||||
protected void onResume() {
|
||||
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onResume " + this);
|
||||
getApplication().dispatchActivityResumed(this);
|
||||
mCalledActivityOptions = null;
|
||||
mCalled = true;
|
||||
}
|
||||
|
||||
@@ -1279,6 +1270,7 @@ public class Activity extends ContextThemeWrapper
|
||||
final void performSaveInstanceState(Bundle outState) {
|
||||
onSaveInstanceState(outState);
|
||||
saveManagedDialogs(outState);
|
||||
mActivityTransitionState.saveState(outState);
|
||||
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onSaveInstanceState " + this + ": " + outState);
|
||||
}
|
||||
|
||||
@@ -1549,10 +1541,7 @@ public class Activity extends ContextThemeWrapper
|
||||
protected void onStop() {
|
||||
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
|
||||
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
|
||||
if (mCalledActivityOptions != null) {
|
||||
mCalledActivityOptions.dispatchActivityStopped();
|
||||
mCalledActivityOptions = null;
|
||||
}
|
||||
mActivityTransitionState.onStop();
|
||||
getApplication().dispatchActivityStopped(this);
|
||||
mTranslucentCallback = null;
|
||||
mCalled = true;
|
||||
@@ -3650,7 +3639,7 @@ public class Activity extends ContextThemeWrapper
|
||||
public void startActivityForResult(Intent intent, int requestCode) {
|
||||
Bundle options = null;
|
||||
if (mWindow.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
|
||||
options = ActivityOptions.makeSceneTransitionAnimation(mWindow, null).toBundle();
|
||||
options = ActivityOptions.makeSceneTransitionAnimation(this).toBundle();
|
||||
}
|
||||
startActivityForResult(intent, requestCode, options);
|
||||
}
|
||||
@@ -3691,9 +3680,7 @@ public class Activity extends ContextThemeWrapper
|
||||
*/
|
||||
public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
|
||||
if (options != null) {
|
||||
ActivityOptions activityOptions = new ActivityOptions(options);
|
||||
activityOptions.dispatchStartExit();
|
||||
mCalledActivityOptions = activityOptions;
|
||||
mActivityTransitionState.startExitOutTransition(this, options);
|
||||
}
|
||||
if (mParent == null) {
|
||||
Instrumentation.ActivityResult ar =
|
||||
@@ -4559,13 +4546,10 @@ public class Activity extends ContextThemeWrapper
|
||||
* to reverse its exit Transition. When the exit Transition completes,
|
||||
* {@link #finish()} is called. If no entry Transition was used, finish() is called
|
||||
* immediately and the Activity exit Transition is run.
|
||||
* @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
|
||||
* android.app.ActivityOptions.ActivityTransitionListener)
|
||||
* @see android.app.ActivityOptions#makeSceneTransitionAnimation(Activity, android.util.Pair[])
|
||||
*/
|
||||
public void finishWithTransition() {
|
||||
if (mEnterTransitionCoordinator != null) {
|
||||
mEnterTransitionCoordinator.startExit();
|
||||
} else {
|
||||
if (!mActivityTransitionState.startExitBackTransition(this)) {
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -4642,6 +4626,27 @@ public class Activity extends ContextThemeWrapper
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when an activity you launched with an activity transition exposes this
|
||||
* Activity through a returning activity transition, giving you the resultCode
|
||||
* and any additional data from it. This method will only be called if the activity
|
||||
* set a result code other than {@link #RESULT_CANCELED} and it supports activity
|
||||
* transitions with {@link Window#FEATURE_CONTENT_TRANSITIONS}.
|
||||
*
|
||||
* <p>The purpose of this function is to let the called Activity send a hint about
|
||||
* its state so that this underlying Activity can prepare to be exposed. A call to
|
||||
* this method does not guarantee that the called Activity has or will be exiting soon.
|
||||
* It only indicates that it will expose this Activity's Window and it has
|
||||
* some data to pass to prepare it.</p>
|
||||
*
|
||||
* @param resultCode The integer result code returned by the child activity
|
||||
* through its setResult().
|
||||
* @param data An Intent, which can return result data to the caller
|
||||
* (various data can be attached to Intent "extras").
|
||||
*/
|
||||
protected void onActivityReenter(int resultCode, Intent data) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new PendingIntent object which you can hand to others
|
||||
* for them to use to send result data back to your
|
||||
@@ -5246,7 +5251,8 @@ public class Activity extends ContextThemeWrapper
|
||||
* This call has no effect on non-translucent activities or on activities with the
|
||||
* {@link android.R.attr#windowIsFloating} attribute.
|
||||
*
|
||||
* @see #convertToTranslucent(TranslucentConversionListener)
|
||||
* @see #convertToTranslucent(android.app.Activity.TranslucentConversionListener,
|
||||
* ActivityOptions)
|
||||
* @see TranslucentConversionListener
|
||||
*
|
||||
* @hide
|
||||
@@ -5544,18 +5550,18 @@ public class Activity extends ContextThemeWrapper
|
||||
}
|
||||
|
||||
/**
|
||||
* When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
|
||||
* android.app.ActivityOptions.ActivityTransitionListener)} was used to start an Activity,
|
||||
* the Window will be triggered to enter with a Transition. <code>listener</code> allows
|
||||
* The Activity to listen to events of the entering transition and control the mapping of
|
||||
* shared elements. This requires {@link Window#FEATURE_CONTENT_TRANSITIONS}.
|
||||
* When {@link android.app.ActivityOptions#makeSceneTransitionAnimation(Activity,
|
||||
* android.view.View, String)} was used to start an Activity, <var>listener</var>
|
||||
* will be called to handle shared elements. This requires
|
||||
* {@link Window#FEATURE_CONTENT_TRANSITIONS}.
|
||||
*
|
||||
* @param listener Used to listen to events in the entering transition.
|
||||
* @param listener Used to manipulate how shared element transitions function.
|
||||
*/
|
||||
public void setActivityTransitionListener(ActivityOptions.ActivityTransitionListener listener) {
|
||||
if (mEnterTransitionCoordinator != null) {
|
||||
mEnterTransitionCoordinator.setActivityTransitionListener(listener);
|
||||
public void setSharedElementListener(SharedElementListener listener) {
|
||||
if (listener == null) {
|
||||
listener = new SharedElementListener();
|
||||
}
|
||||
mTransitionListener = listener;
|
||||
}
|
||||
|
||||
// ------------------ Internal API ------------------
|
||||
@@ -5621,19 +5627,23 @@ public class Activity extends ContextThemeWrapper
|
||||
mVisibleFromClient = !mWindow.getWindowStyle().getBoolean(
|
||||
com.android.internal.R.styleable.Window_windowNoDisplay, false);
|
||||
mFragments.dispatchActivityCreated();
|
||||
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
|
||||
}
|
||||
|
||||
final void performCreate(Bundle icicle) {
|
||||
onCreate(icicle);
|
||||
mActivityTransitionState.readState(icicle);
|
||||
performCreateCommon();
|
||||
}
|
||||
|
||||
final void performCreate(Bundle icicle, PersistableBundle persistentState) {
|
||||
onCreate(icicle, persistentState);
|
||||
mActivityTransitionState.readState(icicle);
|
||||
performCreateCommon();
|
||||
}
|
||||
|
||||
final void performStart() {
|
||||
mActivityTransitionState.setEnterActivityOptions(this, getActivityOptions());
|
||||
mFragments.noteStateNotSaved();
|
||||
mCalled = false;
|
||||
mFragments.execPendingActions();
|
||||
@@ -5656,6 +5666,7 @@ public class Activity extends ContextThemeWrapper
|
||||
lm.doReportStart();
|
||||
}
|
||||
}
|
||||
mActivityTransitionState.enterReady(this);
|
||||
}
|
||||
|
||||
final void performRestart() {
|
||||
|
||||
@@ -17,18 +17,18 @@
|
||||
package android.app;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.IRemoteCallback;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.transition.Transition;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -108,6 +108,12 @@ public class ActivityOptions {
|
||||
private static final String KEY_TRANSITION_COMPLETE_LISTENER
|
||||
= "android:transitionCompleteListener";
|
||||
|
||||
private static final String KEY_TRANSITION_IS_RETURNING = "android:transitionIsReturning";
|
||||
private static final String KEY_TRANSITION_SHARED_ELEMENTS = "android:sharedElementNames";
|
||||
private static final String KEY_LOCAL_SHARED_ELEMENTS = "android:localSharedElementNames";
|
||||
private static final String KEY_RESULT_DATA = "android:resultData";
|
||||
private static final String KEY_RESULT_CODE = "android:resultCode";
|
||||
|
||||
/** @hide */
|
||||
public static final int ANIM_NONE = 0;
|
||||
/** @hide */
|
||||
@@ -131,7 +137,12 @@ public class ActivityOptions {
|
||||
private int mStartWidth;
|
||||
private int mStartHeight;
|
||||
private IRemoteCallback mAnimationStartedListener;
|
||||
private ResultReceiver mExitReceiver;
|
||||
private ResultReceiver mTransitionReceiver;
|
||||
private boolean mIsReturning;
|
||||
private ArrayList<String> mSharedElementNames;
|
||||
private ArrayList<String> mLocalSharedElementNames;
|
||||
private Intent mResultData;
|
||||
private int mResultCode;
|
||||
|
||||
/**
|
||||
* Create an ActivityOptions specifying a custom animation to run when
|
||||
@@ -334,7 +345,7 @@ public class ActivityOptions {
|
||||
* <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
|
||||
* enabled on the calling Activity to cause an exit transition. The same must be in
|
||||
* the called Activity to get an entering transition.</p>
|
||||
* @param window The window containing shared elements.
|
||||
* @param activity The Activity whose window contains the shared elements.
|
||||
* @param sharedElement The View to transition to the started Activity. sharedElement must
|
||||
* have a non-null sharedElementName.
|
||||
* @param sharedElementName The shared element name as used in the target Activity. This may
|
||||
@@ -344,40 +355,70 @@ public class ActivityOptions {
|
||||
* @see android.transition.Transition#setEpicenterCallback(
|
||||
* android.transition.Transition.EpicenterCallback)
|
||||
*/
|
||||
public static ActivityOptions makeSceneTransitionAnimation(Window window,
|
||||
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
|
||||
View sharedElement, String sharedElementName) {
|
||||
return makeSceneTransitionAnimation(window,
|
||||
new SharedElementMappingListener(sharedElement, sharedElementName));
|
||||
return makeSceneTransitionAnimation(activity, Pair.create(sharedElement, sharedElementName));
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an ActivityOptions to transition between Activities using cross-Activity scene
|
||||
* animations. This method carries the position of multiple shared elements to the started
|
||||
* Activity. The position of the first element in the value returned from
|
||||
* {@link android.app.ActivityOptions.ActivityTransitionListener#getSharedElementsMapping()}
|
||||
* Activity. The position of the first element in sharedElements
|
||||
* will be used as the epicenter for the exit Transition. The position of the associated
|
||||
* shared element in the launched Activity will be the epicenter of its entering Transition.
|
||||
*
|
||||
* <p>This requires {@link android.view.Window#FEATURE_CONTENT_TRANSITIONS} to be
|
||||
* enabled on the calling Activity to cause an exit transition. The same must be in
|
||||
* the called Activity to get an entering transition.</p>
|
||||
* @param window The window containing shared elements.
|
||||
* @param listener The listener to use to monitor activity transition events.
|
||||
* @param activity The Activity whose window contains the shared elements.
|
||||
* @param sharedElements The names of the shared elements to transfer to the called
|
||||
* Activity and their associated Views. The Views must each have
|
||||
* a unique shared element name.
|
||||
* @return Returns a new ActivityOptions object that you can use to
|
||||
* supply these options as the options Bundle when starting an activity.
|
||||
* Returns null if the Window does not have {@link Window#FEATURE_CONTENT_TRANSITIONS}.
|
||||
* @see android.transition.Transition#setEpicenterCallback(
|
||||
* android.transition.Transition.EpicenterCallback)
|
||||
*/
|
||||
public static ActivityOptions makeSceneTransitionAnimation(Window window,
|
||||
ActivityTransitionListener listener) {
|
||||
if (!window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
|
||||
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
|
||||
Pair<View, String>... sharedElements) {
|
||||
if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
|
||||
return null;
|
||||
}
|
||||
ActivityOptions opts = new ActivityOptions();
|
||||
opts.mAnimationType = ANIM_SCENE_TRANSITION;
|
||||
ExitTransitionCoordinator exit = new ExitTransitionCoordinator(window, listener);
|
||||
opts.mExitReceiver = exit;
|
||||
|
||||
ArrayList<String> names = new ArrayList<String>();
|
||||
ArrayList<String> mappedNames = new ArrayList<String>();
|
||||
|
||||
if (sharedElements != null) {
|
||||
for (int i = 0; i < sharedElements.length; i++) {
|
||||
Pair<View, String> sharedElement = sharedElements[i];
|
||||
names.add(sharedElement.second);
|
||||
mappedNames.add(sharedElement.first.getViewName());
|
||||
}
|
||||
}
|
||||
|
||||
ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, names, names,
|
||||
mappedNames, false);
|
||||
opts.mTransitionReceiver = exit;
|
||||
opts.mSharedElementNames = names;
|
||||
opts.mLocalSharedElementNames = mappedNames;
|
||||
opts.mIsReturning = false;
|
||||
return opts;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
|
||||
ExitTransitionCoordinator exitCoordinator, ArrayList<String> sharedElementNames,
|
||||
int resultCode, Intent resultData) {
|
||||
ActivityOptions opts = new ActivityOptions();
|
||||
opts.mAnimationType = ANIM_SCENE_TRANSITION;
|
||||
opts.mSharedElementNames = sharedElementNames;
|
||||
opts.mTransitionReceiver = exitCoordinator;
|
||||
opts.mIsReturning = true;
|
||||
opts.mResultCode = resultCode;
|
||||
opts.mResultData = resultData;
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -413,7 +454,12 @@ public class ActivityOptions {
|
||||
break;
|
||||
|
||||
case ANIM_SCENE_TRANSITION:
|
||||
mExitReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
|
||||
mTransitionReceiver = opts.getParcelable(KEY_TRANSITION_COMPLETE_LISTENER);
|
||||
mIsReturning = opts.getBoolean(KEY_TRANSITION_IS_RETURNING, false);
|
||||
mSharedElementNames = opts.getStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS);
|
||||
mLocalSharedElementNames = opts.getStringArrayList(KEY_LOCAL_SHARED_ELEMENTS);
|
||||
mResultData = opts.getParcelable(KEY_RESULT_DATA);
|
||||
mResultCode = opts.getInt(KEY_RESULT_CODE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -470,15 +516,15 @@ public class ActivityOptions {
|
||||
|
||||
/** @hide */
|
||||
public void dispatchActivityStopped() {
|
||||
if (mExitReceiver != null) {
|
||||
mExitReceiver.send(ActivityTransitionCoordinator.MSG_ACTIVITY_STOPPED, null);
|
||||
if (mTransitionReceiver != null) {
|
||||
mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_ACTIVITY_STOPPED, null);
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void dispatchStartExit() {
|
||||
if (mExitReceiver != null) {
|
||||
mExitReceiver.send(ActivityTransitionCoordinator.MSG_START_EXIT_TRANSITION, null);
|
||||
if (mTransitionReceiver != null) {
|
||||
mTransitionReceiver.send(ActivityTransitionCoordinator.MSG_START_EXIT_TRANSITION, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -492,6 +538,33 @@ public class ActivityOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public void setReturning() {
|
||||
mIsReturning = true;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public boolean isReturning() {
|
||||
return mIsReturning;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public ArrayList<String> getSharedElementNames() {
|
||||
return mSharedElementNames;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public ArrayList<String> getLocalSharedElementNames() { return mLocalSharedElementNames; }
|
||||
|
||||
/** @hide */
|
||||
public ResultReceiver getResultReceiver() { return mTransitionReceiver; }
|
||||
|
||||
/** @hide */
|
||||
public int getResultCode() { return mResultCode; }
|
||||
|
||||
/** @hide */
|
||||
public Intent getResultData() { return mResultData; }
|
||||
|
||||
/** @hide */
|
||||
public static void abort(Bundle options) {
|
||||
if (options != null) {
|
||||
@@ -499,15 +572,6 @@ public class ActivityOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public EnterTransitionCoordinator createEnterActivityTransition(Activity activity) {
|
||||
EnterTransitionCoordinator coordinator = null;
|
||||
if (mAnimationType == ANIM_SCENE_TRANSITION) {
|
||||
coordinator = new EnterTransitionCoordinator(activity, mExitReceiver);
|
||||
}
|
||||
return coordinator;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the current values in this ActivityOptions from those supplied
|
||||
* in <var>otherOptions</var>. Any values
|
||||
@@ -517,7 +581,12 @@ public class ActivityOptions {
|
||||
if (otherOptions.mPackageName != null) {
|
||||
mPackageName = otherOptions.mPackageName;
|
||||
}
|
||||
mExitReceiver = null;
|
||||
mTransitionReceiver = null;
|
||||
mSharedElementNames = null;
|
||||
mLocalSharedElementNames = null;
|
||||
mIsReturning = false;
|
||||
mResultData = null;
|
||||
mResultCode = 0;
|
||||
switch (otherOptions.mAnimationType) {
|
||||
case ANIM_CUSTOM:
|
||||
mAnimationType = otherOptions.mAnimationType;
|
||||
@@ -562,9 +631,14 @@ public class ActivityOptions {
|
||||
break;
|
||||
case ANIM_SCENE_TRANSITION:
|
||||
mAnimationType = otherOptions.mAnimationType;
|
||||
mExitReceiver = otherOptions.mExitReceiver;
|
||||
mTransitionReceiver = otherOptions.mTransitionReceiver;
|
||||
mSharedElementNames = otherOptions.mSharedElementNames;
|
||||
mLocalSharedElementNames = otherOptions.mLocalSharedElementNames;
|
||||
mIsReturning = otherOptions.mIsReturning;
|
||||
mThumbnail = null;
|
||||
mAnimationStartedListener = null;
|
||||
mResultData = otherOptions.mResultData;
|
||||
mResultCode = otherOptions.mResultCode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -608,9 +682,14 @@ public class ActivityOptions {
|
||||
break;
|
||||
case ANIM_SCENE_TRANSITION:
|
||||
b.putInt(KEY_ANIM_TYPE, mAnimationType);
|
||||
if (mExitReceiver != null) {
|
||||
b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mExitReceiver);
|
||||
if (mTransitionReceiver != null) {
|
||||
b.putParcelable(KEY_TRANSITION_COMPLETE_LISTENER, mTransitionReceiver);
|
||||
}
|
||||
b.putBoolean(KEY_TRANSITION_IS_RETURNING, mIsReturning);
|
||||
b.putStringArrayList(KEY_TRANSITION_SHARED_ELEMENTS, mSharedElementNames);
|
||||
b.putStringArrayList(KEY_LOCAL_SHARED_ELEMENTS, mLocalSharedElementNames);
|
||||
b.putParcelable(KEY_RESULT_DATA, mResultData);
|
||||
b.putInt(KEY_RESULT_CODE, mResultCode);
|
||||
break;
|
||||
}
|
||||
return b;
|
||||
@@ -630,126 +709,4 @@ public class ActivityOptions {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Listener provided in
|
||||
* {@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
|
||||
* android.app.ActivityOptions.ActivityTransitionListener)} or in
|
||||
* {@link android.app.Activity#setActivityTransitionListener(
|
||||
* android.app.ActivityOptions.ActivityTransitionListener)} to monitor the Activity transitions.
|
||||
* The events can be used to customize or override Activity Transition behavior.
|
||||
*/
|
||||
public static class ActivityTransitionListener {
|
||||
/**
|
||||
* Called when the enter Transition is ready to start, but hasn't started yet. If
|
||||
* {@link android.view.Window#getEnterTransition()} is non-null,
|
||||
* The entering views will be {@link View#INVISIBLE}.
|
||||
*/
|
||||
public void onEnterReady() {}
|
||||
|
||||
/**
|
||||
* Called when the remote exiting transition completes.
|
||||
*/
|
||||
public void onRemoteExitComplete() {}
|
||||
|
||||
/**
|
||||
* Called when the start state for shared elements is captured on enter.
|
||||
*
|
||||
* @param sharedElementNames The names of the shared elements that were accepted into
|
||||
* the View hierarchy.
|
||||
* @param sharedElements The shared elements that are part of the View hierarchy.
|
||||
* @param sharedElementSnapshots The Views containing snap shots of the shared element
|
||||
* from the launching Window. These elements will not
|
||||
* be part of the scene, but will be positioned relative
|
||||
* to the Window decor View.
|
||||
*/
|
||||
public void onCaptureSharedElementStart(List<String> sharedElementNames,
|
||||
List<View> sharedElements, List<View> sharedElementSnapshots) {}
|
||||
|
||||
/**
|
||||
* Called when the end state for shared elements is captured on enter.
|
||||
*
|
||||
* @param sharedElementNames The names of the shared elements that were accepted into
|
||||
* the View hierarchy.
|
||||
* @param sharedElements The shared elements that are part of the View hierarchy.
|
||||
* @param sharedElementSnapshots The Views containing snap shots of the shared element
|
||||
* from the launching Window. These elements will not
|
||||
* be part of the scene, but will be positioned relative
|
||||
* to the Window decor View.
|
||||
*/
|
||||
public void onCaptureSharedElementEnd(List<String> sharedElementNames,
|
||||
List<View> sharedElements, List<View> sharedElementSnapshots) {}
|
||||
|
||||
/**
|
||||
* Called when the enter Transition has been started.
|
||||
* @param sharedElementNames The names of shared elements that were transferred.
|
||||
* @param sharedElements The shared elements that were transferred.
|
||||
*/
|
||||
public void onStartEnterTransition(List<String> sharedElementNames,
|
||||
List<View> sharedElements) {}
|
||||
|
||||
/**
|
||||
* Called when the exit Transition has been started.
|
||||
* @param sharedElementNames The names of all shared elements that will be transferred.
|
||||
* @param sharedElements All shared elements that will be transferred.
|
||||
*/
|
||||
public void onStartExitTransition(List<String> sharedElementNames,
|
||||
List<View> sharedElements) {}
|
||||
|
||||
/**
|
||||
* Called when the exiting shared element transition completes.
|
||||
*/
|
||||
public void onSharedElementExitTransitionComplete() {}
|
||||
|
||||
/**
|
||||
* Called on exit when the shared element has been transferred.
|
||||
* @param sharedElementNames The names of all shared elements that were transferred.
|
||||
* @param sharedElements All shared elements that will were transferred.
|
||||
*/
|
||||
public void onSharedElementTransferred(List<String> sharedElementNames,
|
||||
List<View> sharedElements) {}
|
||||
|
||||
/**
|
||||
* Called when the exit transition has completed.
|
||||
*/
|
||||
public void onExitTransitionComplete() {}
|
||||
|
||||
/**
|
||||
* Returns a mapping from a View in the View hierarchy to the shared element name used
|
||||
* in the call. This is called twice -- once when the view is
|
||||
* entering and again when it exits. A null return value indicates that the
|
||||
* View hierachy can be trusted without any remapping.
|
||||
* @return A map from a View in the hierarchy to the shared element name used in the
|
||||
* call.
|
||||
*/
|
||||
public Pair<View, String>[] getSharedElementsMapping() { return null; }
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if the ActivityTransitionListener will handle removing
|
||||
* rejected shared elements from the scene. If <code>false</code> is returned, a default
|
||||
* animation will be used to remove the rejected shared elements from the scene.
|
||||
*
|
||||
* @param rejectedSharedElements Views containing visual information of shared elements
|
||||
* that are not part of the entering scene. These Views
|
||||
* are positioned relative to the Window decor View.
|
||||
* @return <code>false</code> if the default animation should be used to remove the
|
||||
* rejected shared elements from the scene or <code>true</code> if the listener provides
|
||||
* custom handling.
|
||||
*/
|
||||
public boolean handleRejectedSharedElements(List<View> rejectedSharedElements) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static class SharedElementMappingListener extends ActivityTransitionListener {
|
||||
Pair<View, String>[] mSharedElementsMapping = new Pair[1];
|
||||
|
||||
public SharedElementMappingListener(View view, String name) {
|
||||
mSharedElementsMapping[0] = Pair.create(view, name);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<View, String>[] getSharedElementsMapping() {
|
||||
return mSharedElementsMapping;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,27 +15,14 @@
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.ResultReceiver;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionManager;
|
||||
import android.transition.TransitionSet;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewGroupOverlay;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
@@ -83,14 +70,13 @@ import java.util.Collection;
|
||||
* - onActivityStopped() is called and all exited Views are made VISIBLE.
|
||||
*
|
||||
* Typical finishWithTransition goes like this:
|
||||
* 1) finishWithTransition() calls startExit()
|
||||
* - The Window start transitioning to Translucent
|
||||
* 1) finishWithTransition() creates an ExitTransitionCoordinator and calls startExit()
|
||||
* - The Window start transitioning to Translucent with a new ActivityOptions.
|
||||
* - If no background exists, a black background is substituted
|
||||
* - MSG_PREPARE_RESTORE is sent to the ExitTransitionCoordinator
|
||||
* - The shared elements in the scene are matched against those shared elements
|
||||
* that were sent by comparing the names.
|
||||
* - The exit transition is started by setting Views to INVISIBLE.
|
||||
* 2) MSG_PREPARE_RESTORE is received by the EnterTransitionCoordinator
|
||||
* 2) The ActivityOptions is received by the Activity and an EnterTransitionCoordinator is created.
|
||||
* - All transitioning views are made VISIBLE to reverse what was done when onActivityStopped()
|
||||
* was called
|
||||
* 3) The Window is made translucent and a callback is received
|
||||
@@ -98,52 +84,46 @@ import java.util.Collection;
|
||||
* 4) The background alpha animation completes
|
||||
* 5) The shared element transition completes
|
||||
* - After both 4 & 5 complete, MSG_TAKE_SHARED_ELEMENTS is sent to the
|
||||
* ExitTransitionCoordinator
|
||||
* 6) MSG_TAKE_SHARED_ELEMENTS is received by ExitTransitionCoordinator
|
||||
* EnterTransitionCoordinator
|
||||
* 6) MSG_TAKE_SHARED_ELEMENTS is received by EnterTransitionCoordinator
|
||||
* - Shared elements are made VISIBLE
|
||||
* - Shared elements positions and size are set to match the end state of the calling
|
||||
* Activity.
|
||||
* - The shared element transition is started
|
||||
* - If the window allows overlapping transitions, the views transition is started by setting
|
||||
* the entering Views to VISIBLE.
|
||||
* - MSG_HIDE_SHARED_ELEMENTS is sent to the EnterTransitionCoordinator
|
||||
* 7) MSG_HIDE_SHARED_ELEMENTS is received by the EnterTransitionCoordinator
|
||||
* - MSG_HIDE_SHARED_ELEMENTS is sent to the ExitTransitionCoordinator
|
||||
* 7) MSG_HIDE_SHARED_ELEMENTS is received by the ExitTransitionCoordinator
|
||||
* - The shared elements are made INVISIBLE
|
||||
* 8) The exit transition completes in the finishing Activity.
|
||||
* - MSG_EXIT_TRANSITION_COMPLETE is sent to the ExitTransitionCoordinator.
|
||||
* - MSG_EXIT_TRANSITION_COMPLETE is sent to the EnterTransitionCoordinator.
|
||||
* - finish() is called on the exiting Activity
|
||||
* 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the ExitTransitionCoordinator.
|
||||
* 9) The MSG_EXIT_TRANSITION_COMPLETE is received by the EnterTransitionCoordinator.
|
||||
* - If the window doesn't allow overlapping enter transitions, the enter transition is started
|
||||
* by setting entering views to VISIBLE.
|
||||
*/
|
||||
abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
private static final String TAG = "ActivityTransitionCoordinator";
|
||||
|
||||
/**
|
||||
* The names of shared elements that are transitioned to the started Activity.
|
||||
* This is also the name of shared elements that the started Activity accepted.
|
||||
*/
|
||||
public static final String KEY_SHARED_ELEMENT_NAMES = "android:shared_element_names";
|
||||
|
||||
public static final String KEY_SHARED_ELEMENT_STATE = "android:shared_element_state";
|
||||
|
||||
/**
|
||||
* For Activity transitions, the called Activity's listener to receive calls
|
||||
* when transitions complete.
|
||||
*/
|
||||
static final String KEY_TRANSITION_RESULTS_RECEIVER = "android:transitionTargetListener";
|
||||
static final String KEY_REMOTE_RECEIVER = "android:remoteReceiver";
|
||||
|
||||
private static final String KEY_SCREEN_X = "shared_element:screenX";
|
||||
private static final String KEY_SCREEN_Y = "shared_element:screenY";
|
||||
private static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
|
||||
private static final String KEY_WIDTH = "shared_element:width";
|
||||
private static final String KEY_HEIGHT = "shared_element:height";
|
||||
private static final String KEY_NAME = "shared_element:name";
|
||||
private static final String KEY_BITMAP = "shared_element:bitmap";
|
||||
private static final String KEY_SCALE_TYPE = "shared_element:scaleType";
|
||||
private static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
|
||||
protected static final String KEY_SCREEN_X = "shared_element:screenX";
|
||||
protected static final String KEY_SCREEN_Y = "shared_element:screenY";
|
||||
protected static final String KEY_TRANSLATION_Z = "shared_element:translationZ";
|
||||
protected static final String KEY_WIDTH = "shared_element:width";
|
||||
protected static final String KEY_HEIGHT = "shared_element:height";
|
||||
protected static final String KEY_BITMAP = "shared_element:bitmap";
|
||||
protected static final String KEY_SCALE_TYPE = "shared_element:scaleType";
|
||||
protected static final String KEY_IMAGE_MATRIX = "shared_element:imageMatrix";
|
||||
|
||||
private static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
|
||||
// The background fade in/out duration. 150ms is pretty quick, but not abrupt.
|
||||
public static final int FADE_BACKGROUND_DURATION_MS = 150;
|
||||
|
||||
protected static final ImageView.ScaleType[] SCALE_TYPE_VALUES = ImageView.ScaleType.values();
|
||||
|
||||
/**
|
||||
* Sent by the exiting coordinator (either EnterTransitionCoordinator
|
||||
@@ -154,7 +134,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
* until this message is received, but may wait for
|
||||
* MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
|
||||
*/
|
||||
public static final int MSG_SET_LISTENER = 100;
|
||||
public static final int MSG_SET_REMOTE_RECEIVER = 100;
|
||||
|
||||
/**
|
||||
* Sent by the entering coordinator to tell the exiting coordinator
|
||||
@@ -164,18 +144,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
*/
|
||||
public static final int MSG_HIDE_SHARED_ELEMENTS = 101;
|
||||
|
||||
/**
|
||||
* Sent by the EnterTransitionCoordinator to tell the
|
||||
* ExitTransitionCoordinator to hide all of its exited views after
|
||||
* MSG_ACTIVITY_STOPPED has caused them all to show.
|
||||
*/
|
||||
public static final int MSG_PREPARE_RESTORE = 102;
|
||||
|
||||
/**
|
||||
* Sent by the exiting Activity in ActivityOptions#dispatchActivityStopped
|
||||
* to leave the Activity in a good state after it has been hidden.
|
||||
*/
|
||||
public static final int MSG_ACTIVITY_STOPPED = 103;
|
||||
public static final int MSG_ACTIVITY_STOPPED = 102;
|
||||
|
||||
/**
|
||||
* Sent by the exiting coordinator (either EnterTransitionCoordinator
|
||||
@@ -186,7 +159,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
* until this message is received, but may wait for
|
||||
* MSG_EXIT_TRANSITION_COMPLETE if allowOverlappingTransitions() is true.
|
||||
*/
|
||||
public static final int MSG_TAKE_SHARED_ELEMENTS = 104;
|
||||
public static final int MSG_TAKE_SHARED_ELEMENTS = 103;
|
||||
|
||||
/**
|
||||
* Sent by the exiting coordinator (either
|
||||
@@ -196,309 +169,41 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
* remote coordinator. If it is false, it will trigger the enter
|
||||
* transition to start.
|
||||
*/
|
||||
public static final int MSG_EXIT_TRANSITION_COMPLETE = 105;
|
||||
public static final int MSG_EXIT_TRANSITION_COMPLETE = 104;
|
||||
|
||||
/**
|
||||
* Sent by Activity#startActivity to begin the exit transition.
|
||||
*/
|
||||
public static final int MSG_START_EXIT_TRANSITION = 106;
|
||||
public static final int MSG_START_EXIT_TRANSITION = 105;
|
||||
|
||||
private Window mWindow;
|
||||
private ArrayList<View> mSharedElements = new ArrayList<View>();
|
||||
private ArrayList<String> mTargetSharedNames = new ArrayList<String>();
|
||||
private ActivityOptions.ActivityTransitionListener mListener =
|
||||
new ActivityOptions.ActivityTransitionListener();
|
||||
private ArrayList<View> mEnteringViews;
|
||||
private ResultReceiver mRemoteResultReceiver;
|
||||
private boolean mNotifiedSharedElementTransitionComplete;
|
||||
private boolean mNotifiedExitTransitionComplete;
|
||||
private boolean mSharedElementTransitionStarted;
|
||||
/**
|
||||
* It took too long for a message from the entering Activity, so we canceled the transition.
|
||||
*/
|
||||
public static final int MSG_CANCEL = 106;
|
||||
|
||||
private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
|
||||
final private Window mWindow;
|
||||
final protected ArrayList<String> mAllSharedElementNames;
|
||||
final protected ArrayList<View> mSharedElements = new ArrayList<View>();
|
||||
final protected ArrayList<String> mSharedElementNames = new ArrayList<String>();
|
||||
final protected ArrayList<View> mTransitioningViews = new ArrayList<View>();
|
||||
final protected SharedElementListener mListener;
|
||||
protected ResultReceiver mResultReceiver;
|
||||
final private FixedEpicenterCallback mEpicenterCallback = new FixedEpicenterCallback();
|
||||
|
||||
private Transition.TransitionListener mSharedElementListener =
|
||||
new Transition.TransitionListenerAdapter() {
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
transition.removeListener(this);
|
||||
onSharedElementTransitionEnd();
|
||||
}
|
||||
};
|
||||
|
||||
private Transition.TransitionListener mExitListener =
|
||||
new Transition.TransitionListenerAdapter() {
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
transition.removeListener(this);
|
||||
onExitTransitionEnd();
|
||||
}
|
||||
};
|
||||
|
||||
public ActivityTransitionCoordinator(Window window)
|
||||
{
|
||||
public ActivityTransitionCoordinator(Window window,
|
||||
ArrayList<String> allSharedElementNames,
|
||||
ArrayList<String> accepted, ArrayList<String> localNames,
|
||||
SharedElementListener listener) {
|
||||
super(new Handler());
|
||||
mWindow = window;
|
||||
}
|
||||
|
||||
// -------------------- ResultsReceiver Overrides ----------------------
|
||||
@Override
|
||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||
switch (resultCode) {
|
||||
case MSG_SET_LISTENER:
|
||||
ResultReceiver resultReceiver
|
||||
= resultData.getParcelable(KEY_TRANSITION_RESULTS_RECEIVER);
|
||||
setRemoteResultReceiver(resultReceiver);
|
||||
onSetResultReceiver();
|
||||
break;
|
||||
case MSG_HIDE_SHARED_ELEMENTS:
|
||||
onHideSharedElements();
|
||||
break;
|
||||
case MSG_PREPARE_RESTORE:
|
||||
onPrepareRestore();
|
||||
break;
|
||||
case MSG_EXIT_TRANSITION_COMPLETE:
|
||||
if (!mSharedElementTransitionStarted) {
|
||||
send(resultCode, resultData);
|
||||
} else {
|
||||
onRemoteSceneExitComplete();
|
||||
}
|
||||
break;
|
||||
case MSG_TAKE_SHARED_ELEMENTS:
|
||||
ArrayList<String> sharedElementNames
|
||||
= resultData.getStringArrayList(KEY_SHARED_ELEMENT_NAMES);
|
||||
Bundle sharedElementState = resultData.getBundle(KEY_SHARED_ELEMENT_STATE);
|
||||
onTakeSharedElements(sharedElementNames, sharedElementState);
|
||||
break;
|
||||
case MSG_ACTIVITY_STOPPED:
|
||||
onActivityStopped();
|
||||
break;
|
||||
case MSG_START_EXIT_TRANSITION:
|
||||
startExit();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------- calls that can be overridden by subclasses --------------------
|
||||
|
||||
/**
|
||||
* Called when MSG_SET_LISTENER is received. This will only be received by
|
||||
* ExitTransitionCoordinator.
|
||||
*/
|
||||
protected void onSetResultReceiver() {}
|
||||
|
||||
/**
|
||||
* Called when MSG_HIDE_SHARED_ELEMENTS is received
|
||||
*/
|
||||
protected void onHideSharedElements() {
|
||||
setViewVisibility(getSharedElements(), View.INVISIBLE);
|
||||
mListener.onSharedElementTransferred(getSharedElementNames(), getSharedElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when MSG_PREPARE_RESTORE is called. This will only be received by
|
||||
* ExitTransitionCoordinator.
|
||||
*/
|
||||
protected void onPrepareRestore() {
|
||||
mListener.onEnterReady();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when MSG_EXIT_TRANSITION_COMPLETE is received -- the remote coordinator has
|
||||
* completed its exit transition. This can be called by the ExitTransitionCoordinator when
|
||||
* starting an Activity or EnterTransitionCoordinator when called with finishWithTransition.
|
||||
*/
|
||||
protected void onRemoteSceneExitComplete() {
|
||||
if (!allowOverlappingTransitions()) {
|
||||
Transition transition = beginTransition(mEnteringViews, false, true, true);
|
||||
onStartEnterTransition(transition, mEnteringViews);
|
||||
}
|
||||
mListener.onRemoteExitComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when MSG_TAKE_SHARED_ELEMENTS is received. This means that the shared elements are
|
||||
* in a stable state and ready to move to the Window.
|
||||
* @param sharedElementNames The names of the shared elements to move.
|
||||
* @param state Contains the shared element states (size & position)
|
||||
*/
|
||||
protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
|
||||
setSharedElements();
|
||||
reconcileSharedElements(sharedElementNames);
|
||||
mEnteringViews.removeAll(mSharedElements);
|
||||
final ArrayList<View> accepted = new ArrayList<View>();
|
||||
final ArrayList<View> rejected = new ArrayList<View>();
|
||||
createSharedElementImages(accepted, rejected, sharedElementNames, state);
|
||||
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
|
||||
setSharedElementState(state, accepted);
|
||||
handleRejected(rejected);
|
||||
|
||||
mListener = listener;
|
||||
mAllSharedElementNames = allSharedElementNames;
|
||||
setSharedElements(accepted, localNames);
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(mEnteringViews, View.INVISIBLE);
|
||||
getDecor().captureTransitioningViews(mTransitioningViews);
|
||||
mTransitioningViews.removeAll(mSharedElements);
|
||||
}
|
||||
setViewVisibility(mSharedElements, View.VISIBLE);
|
||||
Transition transition = beginTransition(mEnteringViews, true, allowOverlappingTransitions(),
|
||||
true);
|
||||
setOriginalImageViewState(originalImageViewState);
|
||||
|
||||
if (allowOverlappingTransitions()) {
|
||||
onStartEnterTransition(transition, mEnteringViews);
|
||||
}
|
||||
|
||||
mRemoteResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when MSG_ACTIVITY_STOPPED is received. This is received when Activity.onStop is
|
||||
* called after running startActivity* is called using an Activity Transition.
|
||||
*/
|
||||
protected void onActivityStopped() {}
|
||||
|
||||
/**
|
||||
* Called when the start transition is ready to run. This may be immediately after
|
||||
* MSG_TAKE_SHARED_ELEMENTS or MSG_EXIT_TRANSITION_COMPLETE, depending on whether
|
||||
* overlapping transitions are allowed.
|
||||
* @param transition The transition currently started.
|
||||
* @param enteringViews The views entering the scene. This won't include shared elements.
|
||||
*/
|
||||
protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) {
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(enteringViews, View.VISIBLE);
|
||||
}
|
||||
mEnteringViews = null;
|
||||
mListener.onStartEnterTransition(getSharedElementNames(), getSharedElements());
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the exit transition has started.
|
||||
* @param exitingViews The views leaving the scene. This won't include shared elements.
|
||||
*/
|
||||
protected void onStartExitTransition(ArrayList<View> exitingViews) {}
|
||||
|
||||
/**
|
||||
* Called during the exit when the shared element transition has completed.
|
||||
*/
|
||||
protected void onSharedElementTransitionEnd() {
|
||||
Bundle bundle = new Bundle();
|
||||
int[] tempLoc = new int[2];
|
||||
for (int i = 0; i < mSharedElements.size(); i++) {
|
||||
View sharedElement = mSharedElements.get(i);
|
||||
String name = mTargetSharedNames.get(i);
|
||||
captureSharedElementState(sharedElement, name, bundle, tempLoc);
|
||||
}
|
||||
Bundle allValues = new Bundle();
|
||||
allValues.putStringArrayList(KEY_SHARED_ELEMENT_NAMES, getSharedElementNames());
|
||||
allValues.putBundle(KEY_SHARED_ELEMENT_STATE, bundle);
|
||||
sharedElementTransitionComplete(allValues);
|
||||
mListener.onSharedElementExitTransitionComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the shared element transition is complete to pass the shared element state
|
||||
* to the remote coordinator.
|
||||
* @param bundle The Bundle to send to the coordinator containing the shared element state.
|
||||
*/
|
||||
protected abstract void sharedElementTransitionComplete(Bundle bundle);
|
||||
|
||||
/**
|
||||
* Called when the exit transition finishes.
|
||||
*/
|
||||
protected void onExitTransitionEnd() {
|
||||
mListener.onExitTransitionComplete();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to start the exit transition. Launched from ActivityOptions#dispatchStartExit
|
||||
*/
|
||||
protected abstract void startExit();
|
||||
|
||||
/**
|
||||
* A non-null transition indicates that the Views of the Window should be made INVISIBLE.
|
||||
* @return The Transition used to cause transitioning views to either enter or exit the scene.
|
||||
*/
|
||||
protected abstract Transition getViewsTransition();
|
||||
|
||||
/**
|
||||
* @return The Transition used to move the shared elements from the start position and size
|
||||
* to the end position and size.
|
||||
*/
|
||||
protected abstract Transition getSharedElementTransition();
|
||||
|
||||
/**
|
||||
* @return When the enter transition should overlap with the exit transition of the
|
||||
* remote controller.
|
||||
*/
|
||||
protected abstract boolean allowOverlappingTransitions();
|
||||
|
||||
// called by subclasses
|
||||
|
||||
protected void notifySharedElementTransitionComplete(Bundle sharedElements) {
|
||||
if (!mNotifiedSharedElementTransitionComplete) {
|
||||
mNotifiedSharedElementTransitionComplete = true;
|
||||
mRemoteResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, sharedElements);
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyExitTransitionComplete() {
|
||||
if (!mNotifiedExitTransitionComplete) {
|
||||
mNotifiedExitTransitionComplete = true;
|
||||
mRemoteResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
|
||||
}
|
||||
}
|
||||
|
||||
protected void notifyPrepareRestore() {
|
||||
mRemoteResultReceiver.send(MSG_PREPARE_RESTORE, null);
|
||||
}
|
||||
|
||||
protected void setRemoteResultReceiver(ResultReceiver resultReceiver) {
|
||||
mRemoteResultReceiver = resultReceiver;
|
||||
}
|
||||
|
||||
protected void notifySetListener() {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable(KEY_TRANSITION_RESULTS_RECEIVER, this);
|
||||
mRemoteResultReceiver.send(MSG_SET_LISTENER, bundle);
|
||||
}
|
||||
|
||||
protected void setEnteringViews(ArrayList<View> views) {
|
||||
mEnteringViews = views;
|
||||
}
|
||||
|
||||
protected void setSharedElements() {
|
||||
Pair<View, String>[] sharedElements = mListener.getSharedElementsMapping();
|
||||
mSharedElements.clear();
|
||||
mTargetSharedNames.clear();
|
||||
if (sharedElements == null) {
|
||||
ArrayMap<String, View> map = new ArrayMap<String, View>();
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(mEnteringViews, View.VISIBLE);
|
||||
}
|
||||
getDecor().findNamedViews(map);
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(mEnteringViews, View.INVISIBLE);
|
||||
}
|
||||
for (int i = 0; i < map.size(); i++) {
|
||||
View view = map.valueAt(i);
|
||||
String name = map.keyAt(i);
|
||||
mSharedElements.add(view);
|
||||
mTargetSharedNames.add(name);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < sharedElements.length; i++) {
|
||||
Pair<View, String> viewStringPair = sharedElements[i];
|
||||
View view = viewStringPair.first;
|
||||
String name = viewStringPair.second;
|
||||
mSharedElements.add(view);
|
||||
mTargetSharedNames.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected ArrayList<View> getSharedElements() {
|
||||
return mSharedElements;
|
||||
}
|
||||
|
||||
protected ArrayList<String> getSharedElementNames() {
|
||||
return mTargetSharedNames;
|
||||
setEpicenter();
|
||||
}
|
||||
|
||||
protected Window getWindow() {
|
||||
@@ -509,238 +214,43 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
return (mWindow == null) ? null : (ViewGroup) mWindow.getDecorView();
|
||||
}
|
||||
|
||||
protected void startExitTransition(ArrayList<String> sharedElements) {
|
||||
setSharedElements();
|
||||
reconcileSharedElements(sharedElements);
|
||||
ArrayList<View> transitioningViews = captureTransitioningViews();
|
||||
beginTransition(transitioningViews, true, true, false);
|
||||
onStartExitTransition(transitioningViews);
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(transitioningViews, View.INVISIBLE);
|
||||
/**
|
||||
* Sets the transition epicenter to the position of the first shared element.
|
||||
*/
|
||||
protected void setEpicenter() {
|
||||
View epicenter = null;
|
||||
if (!mAllSharedElementNames.isEmpty() && !mSharedElementNames.isEmpty() &&
|
||||
mAllSharedElementNames.get(0).equals(mSharedElementNames.get(0))) {
|
||||
epicenter = mSharedElements.get(0);
|
||||
}
|
||||
mListener.onStartExitTransition(getSharedElementNames(), getSharedElements());
|
||||
setEpicenter(epicenter);
|
||||
}
|
||||
|
||||
protected void clearConnections() {
|
||||
mRemoteResultReceiver = null;
|
||||
}
|
||||
|
||||
// public API
|
||||
|
||||
public void setActivityTransitionListener(ActivityOptions.ActivityTransitionListener listener) {
|
||||
if (listener == null) {
|
||||
mListener = new ActivityOptions.ActivityTransitionListener();
|
||||
private void setEpicenter(View view) {
|
||||
if (view == null) {
|
||||
mEpicenterCallback.setEpicenter(null);
|
||||
} else {
|
||||
mListener = listener;
|
||||
int[] loc = new int[2];
|
||||
view.getLocationOnScreen(loc);
|
||||
int left = loc[0] + Math.round(view.getTranslationX());
|
||||
int top = loc[1] + Math.round(view.getTranslationY());
|
||||
int right = left + view.getWidth();
|
||||
int bottom = top + view.getHeight();
|
||||
Rect epicenter = new Rect(left, top, right, bottom);
|
||||
mEpicenterCallback.setEpicenter(epicenter);
|
||||
}
|
||||
}
|
||||
|
||||
// private methods
|
||||
|
||||
private Transition configureTransition(Transition transition) {
|
||||
if (transition != null) {
|
||||
transition = transition.clone();
|
||||
transition.setEpicenterCallback(mEpicenterCallback);
|
||||
}
|
||||
return transition;
|
||||
public ArrayList<String> getAcceptedNames() {
|
||||
return mSharedElementNames;
|
||||
}
|
||||
|
||||
private void reconcileSharedElements(ArrayList<String> sharedElementNames) {
|
||||
// keep only those that are in sharedElementNames.
|
||||
int numSharedElements = sharedElementNames.size();
|
||||
int targetIndex = 0;
|
||||
for (int i = 0; i < numSharedElements; i++) {
|
||||
String name = sharedElementNames.get(i);
|
||||
int index = mTargetSharedNames.indexOf(name);
|
||||
if (index >= 0) {
|
||||
// Swap the items at the indexes if necessary.
|
||||
if (index != targetIndex) {
|
||||
View temp = mSharedElements.get(index);
|
||||
mSharedElements.set(index, mSharedElements.get(targetIndex));
|
||||
mSharedElements.set(targetIndex, temp);
|
||||
mTargetSharedNames.set(index, mTargetSharedNames.get(targetIndex));
|
||||
mTargetSharedNames.set(targetIndex, name);
|
||||
}
|
||||
targetIndex++;
|
||||
}
|
||||
public ArrayList<String> getMappedNames() {
|
||||
ArrayList<String> names = new ArrayList<String>(mSharedElements.size());
|
||||
for (int i = 0; i < mSharedElements.size(); i++) {
|
||||
names.add(mSharedElements.get(i).getViewName());
|
||||
}
|
||||
for (int i = mSharedElements.size() - 1; i >= targetIndex; i--) {
|
||||
mSharedElements.remove(i);
|
||||
mTargetSharedNames.remove(i);
|
||||
}
|
||||
Rect epicenter = null;
|
||||
if (!mTargetSharedNames.isEmpty()
|
||||
&& mTargetSharedNames.get(0).equals(sharedElementNames.get(0))) {
|
||||
epicenter = calcEpicenter(mSharedElements.get(0));
|
||||
}
|
||||
mEpicenterCallback.setEpicenter(epicenter);
|
||||
}
|
||||
|
||||
private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
|
||||
Bundle sharedElementState, final ArrayList<View> acceptedOverlayViews) {
|
||||
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
|
||||
new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
|
||||
final int[] tempLoc = new int[2];
|
||||
if (sharedElementState != null) {
|
||||
for (int i = 0; i < mSharedElements.size(); i++) {
|
||||
View sharedElement = mSharedElements.get(i);
|
||||
String name = mTargetSharedNames.get(i);
|
||||
Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
|
||||
name, sharedElementState);
|
||||
if (originalState != null) {
|
||||
originalImageState.put((ImageView) sharedElement, originalState);
|
||||
}
|
||||
View parent = (View) sharedElement.getParent();
|
||||
parent.getLocationOnScreen(tempLoc);
|
||||
setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
|
||||
sharedElement.requestLayout();
|
||||
}
|
||||
}
|
||||
mListener.onCaptureSharedElementStart(mTargetSharedNames, mSharedElements,
|
||||
acceptedOverlayViews);
|
||||
|
||||
getDecor().getViewTreeObserver().addOnPreDrawListener(
|
||||
new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
mListener.onCaptureSharedElementEnd(mTargetSharedNames, mSharedElements,
|
||||
acceptedOverlayViews);
|
||||
mSharedElementTransitionStarted = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
);
|
||||
return originalImageState;
|
||||
}
|
||||
|
||||
private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
|
||||
Bundle transitionArgs) {
|
||||
if (!(view instanceof ImageView)) {
|
||||
return null;
|
||||
}
|
||||
Bundle bundle = transitionArgs.getBundle(name);
|
||||
int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
|
||||
if (scaleTypeInt < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ImageView imageView = (ImageView) view;
|
||||
ImageView.ScaleType originalScaleType = imageView.getScaleType();
|
||||
|
||||
Matrix originalMatrix = null;
|
||||
if (originalScaleType == ImageView.ScaleType.MATRIX) {
|
||||
originalMatrix = new Matrix(imageView.getImageMatrix());
|
||||
}
|
||||
|
||||
return Pair.create(originalScaleType, originalMatrix);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the captured values from a previous
|
||||
* {@link #captureSharedElementState(android.view.View, String, android.os.Bundle, int[])}
|
||||
* @param view The View to apply placement changes to.
|
||||
* @param name The shared element name given from the source Activity.
|
||||
* @param transitionArgs A <code>Bundle</code> containing all placementinformation for named
|
||||
* shared elements in the scene.
|
||||
* @param parentLoc The x and y coordinates of the parent's screen position.
|
||||
*/
|
||||
private static void setSharedElementState(View view, String name, Bundle transitionArgs,
|
||||
int[] parentLoc) {
|
||||
Bundle sharedElementBundle = transitionArgs.getBundle(name);
|
||||
if (sharedElementBundle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (view instanceof ImageView) {
|
||||
int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
|
||||
if (scaleTypeInt >= 0) {
|
||||
ImageView imageView = (ImageView) view;
|
||||
ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
|
||||
imageView.setScaleType(scaleType);
|
||||
if (scaleType == ImageView.ScaleType.MATRIX) {
|
||||
float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setValues(matrixValues);
|
||||
imageView.setImageMatrix(matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
|
||||
view.setTranslationZ(z);
|
||||
|
||||
int x = sharedElementBundle.getInt(KEY_SCREEN_X);
|
||||
int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
|
||||
int width = sharedElementBundle.getInt(KEY_WIDTH);
|
||||
int height = sharedElementBundle.getInt(KEY_HEIGHT);
|
||||
|
||||
int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
|
||||
int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
|
||||
view.measure(widthSpec, heightSpec);
|
||||
|
||||
int left = x - parentLoc[0];
|
||||
int top = y - parentLoc[1];
|
||||
int right = left + width;
|
||||
int bottom = top + height;
|
||||
view.layout(left, top, right, bottom);
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures placement information for Views with a shared element name for
|
||||
* Activity Transitions.
|
||||
* @param view The View to capture the placement information for.
|
||||
* @param name The shared element name in the target Activity to apply the placement
|
||||
* information for.
|
||||
* @param transitionArgs Bundle to store shared element placement information.
|
||||
* @param tempLoc A temporary int[2] for capturing the current location of views.
|
||||
* @see #setSharedElementState(android.view.View, String, android.os.Bundle, int[])
|
||||
*/
|
||||
private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
|
||||
int[] tempLoc) {
|
||||
Bundle sharedElementBundle = new Bundle();
|
||||
view.getLocationOnScreen(tempLoc);
|
||||
float scaleX = view.getScaleX();
|
||||
sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
|
||||
int width = Math.round(view.getWidth() * scaleX);
|
||||
sharedElementBundle.putInt(KEY_WIDTH, width);
|
||||
|
||||
float scaleY = view.getScaleY();
|
||||
sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
|
||||
int height= Math.round(view.getHeight() * scaleY);
|
||||
sharedElementBundle.putInt(KEY_HEIGHT, height);
|
||||
|
||||
sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
|
||||
|
||||
sharedElementBundle.putString(KEY_NAME, view.getViewName());
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
view.draw(canvas);
|
||||
sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
|
||||
|
||||
if (view instanceof ImageView) {
|
||||
ImageView imageView = (ImageView) view;
|
||||
int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
|
||||
sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
|
||||
if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
|
||||
float[] matrix = new float[9];
|
||||
imageView.getImageMatrix().getValues(matrix);
|
||||
sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
|
||||
}
|
||||
}
|
||||
|
||||
transitionArgs.putBundle(name, sharedElementBundle);
|
||||
}
|
||||
|
||||
private static Rect calcEpicenter(View view) {
|
||||
int[] loc = new int[2];
|
||||
view.getLocationOnScreen(loc);
|
||||
int left = loc[0] + Math.round(view.getTranslationX());
|
||||
int top = loc[1] + Math.round(view.getTranslationY());
|
||||
int right = left + view.getWidth();
|
||||
int bottom = top + view.getHeight();
|
||||
return new Rect(left, top, right, bottom);
|
||||
return names;
|
||||
}
|
||||
|
||||
public static void setViewVisibility(Collection<View> views, int visibility) {
|
||||
@@ -751,12 +261,12 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
}
|
||||
}
|
||||
|
||||
private static Transition addTransitionTargets(Transition transition, Collection<View> views) {
|
||||
protected static Transition addTargets(Transition transition, Collection<View> views) {
|
||||
if (transition == null || views == null || views.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
TransitionSet set = new TransitionSet();
|
||||
set.addTransition(transition.clone());
|
||||
set.addTransition(transition);
|
||||
if (views != null) {
|
||||
for (View view: views) {
|
||||
set.addTarget(view);
|
||||
@@ -765,152 +275,62 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
|
||||
return set;
|
||||
}
|
||||
|
||||
private ArrayList<View> captureTransitioningViews() {
|
||||
if (getViewsTransition() == null) {
|
||||
return null;
|
||||
}
|
||||
ArrayList<View> transitioningViews = new ArrayList<View>();
|
||||
getDecor().captureTransitioningViews(transitioningViews);
|
||||
transitioningViews.removeAll(getSharedElements());
|
||||
return transitioningViews;
|
||||
}
|
||||
|
||||
private Transition getSharedElementTransition(boolean isEnter) {
|
||||
Transition transition = getSharedElementTransition();
|
||||
if (transition == null) {
|
||||
return null;
|
||||
}
|
||||
transition = configureTransition(transition);
|
||||
if (!isEnter) {
|
||||
transition.addListener(mSharedElementListener);
|
||||
}
|
||||
return transition;
|
||||
}
|
||||
|
||||
private Transition getViewsTransition(ArrayList<View> transitioningViews, boolean isEnter) {
|
||||
Transition transition = getViewsTransition();
|
||||
if (transition == null) {
|
||||
return null;
|
||||
}
|
||||
transition = configureTransition(transition);
|
||||
if (!isEnter) {
|
||||
transition.addListener(mExitListener);
|
||||
}
|
||||
return addTransitionTargets(transition, transitioningViews);
|
||||
}
|
||||
|
||||
private Transition beginTransition(ArrayList<View> transitioningViews,
|
||||
boolean transitionSharedElement, boolean transitionViews, boolean isEnter) {
|
||||
Transition sharedElementTransition = null;
|
||||
if (transitionSharedElement) {
|
||||
sharedElementTransition = getSharedElementTransition(isEnter);
|
||||
if (!isEnter && sharedElementTransition == null) {
|
||||
onSharedElementTransitionEnd();
|
||||
}
|
||||
}
|
||||
Transition viewsTransition = null;
|
||||
if (transitionViews) {
|
||||
viewsTransition = getViewsTransition(transitioningViews, isEnter);
|
||||
if (!isEnter && viewsTransition == null) {
|
||||
onExitTransitionEnd();
|
||||
}
|
||||
}
|
||||
|
||||
Transition transition = null;
|
||||
if (sharedElementTransition == null) {
|
||||
transition = viewsTransition;
|
||||
} else if (viewsTransition == null) {
|
||||
transition = sharedElementTransition;
|
||||
} else {
|
||||
TransitionSet set = new TransitionSet();
|
||||
set.addTransition(sharedElementTransition);
|
||||
set.addTransition(viewsTransition);
|
||||
transition = set;
|
||||
}
|
||||
protected Transition configureTransition(Transition transition) {
|
||||
if (transition != null) {
|
||||
TransitionManager.beginDelayedTransition(getDecor(), transition);
|
||||
if (transitionSharedElement && !mSharedElements.isEmpty()) {
|
||||
mSharedElements.get(0).invalidate();
|
||||
} else if (transitionViews && !transitioningViews.isEmpty()) {
|
||||
transitioningViews.get(0).invalidate();
|
||||
}
|
||||
transition = transition.clone();
|
||||
transition.setEpicenterCallback(mEpicenterCallback);
|
||||
}
|
||||
return transition;
|
||||
}
|
||||
|
||||
private void handleRejected(final ArrayList<View> rejected) {
|
||||
int numRejected = rejected.size();
|
||||
if (numRejected == 0) {
|
||||
return;
|
||||
protected static Transition mergeTransitions(Transition transition1, Transition transition2) {
|
||||
if (transition1 == null) {
|
||||
return transition2;
|
||||
} else if (transition2 == null) {
|
||||
return transition1;
|
||||
} else {
|
||||
TransitionSet transitionSet = new TransitionSet();
|
||||
transitionSet.addTransition(transition1);
|
||||
transitionSet.addTransition(transition2);
|
||||
return transitionSet;
|
||||
}
|
||||
boolean rejectionHandled = mListener.handleRejectedSharedElements(rejected);
|
||||
if (rejectionHandled) {
|
||||
return;
|
||||
}
|
||||
|
||||
ViewGroupOverlay overlay = getDecor().getOverlay();
|
||||
ObjectAnimator animator = null;
|
||||
for (int i = 0; i < numRejected; i++) {
|
||||
View view = rejected.get(i);
|
||||
overlay.add(view);
|
||||
animator = ObjectAnimator.ofFloat(view, View.ALPHA, 1, 0);
|
||||
animator.start();
|
||||
}
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
ViewGroupOverlay overlay = getDecor().getOverlay();
|
||||
for (int i = rejected.size() - 1; i >= 0; i--) {
|
||||
overlay.remove(rejected.get(i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void createSharedElementImages(ArrayList<View> accepted, ArrayList<View> rejected,
|
||||
ArrayList<String> sharedElementNames, Bundle state) {
|
||||
int numSharedElements = sharedElementNames.size();
|
||||
Context context = getWindow().getContext();
|
||||
int[] parentLoc = new int[2];
|
||||
getDecor().getLocationOnScreen(parentLoc);
|
||||
for (int i = 0; i < numSharedElements; i++) {
|
||||
String name = sharedElementNames.get(i);
|
||||
Bundle sharedElementBundle = state.getBundle(name);
|
||||
if (sharedElementBundle != null) {
|
||||
Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
|
||||
ImageView imageView = new ImageView(context);
|
||||
imageView.setId(com.android.internal.R.id.shared_element);
|
||||
imageView.setScaleType(ImageView.ScaleType.CENTER);
|
||||
imageView.setImageBitmap(bitmap);
|
||||
imageView.setViewName(name);
|
||||
setSharedElementState(imageView, name, state, parentLoc);
|
||||
if (mTargetSharedNames.contains(name)) {
|
||||
accepted.add(imageView);
|
||||
} else {
|
||||
rejected.add(imageView);
|
||||
private void setSharedElements(ArrayList<String> accepted, ArrayList<String> localNames) {
|
||||
if (!mAllSharedElementNames.isEmpty()) {
|
||||
ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
|
||||
getDecor().findNamedViews(sharedElements);
|
||||
if (accepted != null) {
|
||||
for (int i = 0; i < localNames.size(); i++) {
|
||||
String localName = localNames.get(i);
|
||||
String acceptedName = accepted.get(i);
|
||||
if (!localName.equals(acceptedName)) {
|
||||
View view = sharedElements.remove(localName);
|
||||
if (view != null) {
|
||||
sharedElements.put(acceptedName, view);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
sharedElements.retainAll(mAllSharedElementNames);
|
||||
mListener.remapSharedElements(mAllSharedElementNames, sharedElements);
|
||||
sharedElements.retainAll(mAllSharedElementNames);
|
||||
for (int i = 0; i < mAllSharedElementNames.size(); i++) {
|
||||
String name = mAllSharedElementNames.get(i);
|
||||
View sharedElement = sharedElements.get(name);
|
||||
if (sharedElement != null) {
|
||||
mSharedElementNames.add(name);
|
||||
mSharedElements.add(sharedElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void setOriginalImageViewState(
|
||||
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
|
||||
for (int i = 0; i < originalState.size(); i++) {
|
||||
ImageView imageView = originalState.keyAt(i);
|
||||
Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
|
||||
imageView.setScaleType(state.first);
|
||||
imageView.setImageMatrix(state.second);
|
||||
}
|
||||
protected void setResultReceiver(ResultReceiver resultReceiver) {
|
||||
mResultReceiver = resultReceiver;
|
||||
}
|
||||
|
||||
private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
|
||||
for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
|
||||
if (scaleType == SCALE_TYPE_VALUES[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
protected abstract Transition getViewsTransition();
|
||||
|
||||
private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
|
||||
private Rect mEpicenter;
|
||||
|
||||
204
core/java/android/app/ActivityTransitionState.java
Normal file
204
core/java/android/app/ActivityTransitionState.java
Normal file
@@ -0,0 +1,204 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.ResultReceiver;
|
||||
import android.util.ArrayMap;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* This class contains all persistence-related functionality for Activity Transitions.
|
||||
* Activities start exit and enter Activity Transitions through this class.
|
||||
*/
|
||||
class ActivityTransitionState {
|
||||
|
||||
private static final String ENTERING_SHARED_ELEMENTS = "android:enteringSharedElements";
|
||||
|
||||
private static final String ENTERING_MAPPED_FROM = "android:enteringMappedFrom";
|
||||
|
||||
private static final String ENTERING_MAPPED_TO = "android:enteringMappedTo";
|
||||
|
||||
private static final String EXITING_MAPPED_FROM = "android:exitingMappedFrom";
|
||||
|
||||
private static final String EXITING_MAPPED_TO = "android:exitingMappedTo";
|
||||
|
||||
/**
|
||||
* The shared elements that the calling Activity has said that they transferred to this
|
||||
* Activity.
|
||||
*/
|
||||
private ArrayList<String> mEnteringNames;
|
||||
|
||||
/**
|
||||
* The shared elements that this Activity as accepted and mapped to local Views.
|
||||
*/
|
||||
private ArrayList<String> mEnteringFrom;
|
||||
|
||||
/**
|
||||
* The names of local Views that are mapped to those elements in mEnteringFrom.
|
||||
*/
|
||||
private ArrayList<String> mEnteringTo;
|
||||
|
||||
/**
|
||||
* The names of shared elements that were shared to the called Activity.
|
||||
*/
|
||||
private ArrayList<String> mExitingFrom;
|
||||
|
||||
/**
|
||||
* The names of local Views that were shared out, mapped to those elements in mExitingFrom.
|
||||
*/
|
||||
private ArrayList<String> mExitingTo;
|
||||
|
||||
/**
|
||||
* The ActivityOptions used to call an Activity. Used to make the elements restore
|
||||
* Visibility of exited Views.
|
||||
*/
|
||||
private ActivityOptions mCalledActivityOptions;
|
||||
|
||||
/**
|
||||
* We must be able to cancel entering transitions to stop changing the Window to
|
||||
* opaque when we exit before making the Window opaque.
|
||||
*/
|
||||
private EnterTransitionCoordinator mEnterTransitionCoordinator;
|
||||
|
||||
/**
|
||||
* ActivityOptions used on entering this Activity.
|
||||
*/
|
||||
private ActivityOptions mEnterActivityOptions;
|
||||
|
||||
/**
|
||||
* Has an exit transition been started? If so, we don't want to double-exit.
|
||||
*/
|
||||
private boolean mHasExited;
|
||||
|
||||
public ActivityTransitionState() {
|
||||
}
|
||||
|
||||
public void readState(Bundle bundle) {
|
||||
if (bundle != null) {
|
||||
if (mEnterTransitionCoordinator == null || mEnterTransitionCoordinator.isReturning()) {
|
||||
mEnteringNames = bundle.getStringArrayList(ENTERING_SHARED_ELEMENTS);
|
||||
mEnteringFrom = bundle.getStringArrayList(ENTERING_MAPPED_FROM);
|
||||
mEnteringTo = bundle.getStringArrayList(ENTERING_MAPPED_TO);
|
||||
}
|
||||
if (mEnterTransitionCoordinator == null) {
|
||||
mExitingFrom = bundle.getStringArrayList(EXITING_MAPPED_FROM);
|
||||
mExitingTo = bundle.getStringArrayList(EXITING_MAPPED_TO);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void saveState(Bundle bundle) {
|
||||
if (mEnteringNames != null) {
|
||||
bundle.putStringArrayList(ENTERING_SHARED_ELEMENTS, mEnteringNames);
|
||||
bundle.putStringArrayList(ENTERING_MAPPED_FROM, mEnteringFrom);
|
||||
bundle.putStringArrayList(ENTERING_MAPPED_TO, mEnteringTo);
|
||||
}
|
||||
if (mExitingFrom != null) {
|
||||
bundle.putStringArrayList(EXITING_MAPPED_FROM, mExitingFrom);
|
||||
bundle.putStringArrayList(EXITING_MAPPED_TO, mExitingTo);
|
||||
}
|
||||
}
|
||||
|
||||
public void setEnterActivityOptions(Activity activity, ActivityOptions options) {
|
||||
if (activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)
|
||||
&& options != null && mEnterActivityOptions == null
|
||||
&& options.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
|
||||
mEnterActivityOptions = options;
|
||||
if (mEnterActivityOptions.isReturning()) {
|
||||
int result = mEnterActivityOptions.getResultCode();
|
||||
if (result != 0) {
|
||||
activity.onActivityReenter(result, mEnterActivityOptions.getResultData());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void enterReady(Activity activity) {
|
||||
if (mEnterActivityOptions == null) {
|
||||
return;
|
||||
}
|
||||
mHasExited = false;
|
||||
ArrayList<String> sharedElementNames = mEnterActivityOptions.getSharedElementNames();
|
||||
ResultReceiver resultReceiver = mEnterActivityOptions.getResultReceiver();
|
||||
if (mEnterActivityOptions.isReturning()) {
|
||||
if (mCalledActivityOptions != null) {
|
||||
mCalledActivityOptions.dispatchActivityStopped();
|
||||
mCalledActivityOptions = null;
|
||||
}
|
||||
activity.getWindow().getDecorView().setVisibility(View.VISIBLE);
|
||||
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
|
||||
resultReceiver, sharedElementNames, mExitingFrom, mExitingTo);
|
||||
} else {
|
||||
mEnterTransitionCoordinator = new EnterTransitionCoordinator(activity,
|
||||
resultReceiver, sharedElementNames, null, null);
|
||||
mEnteringNames = sharedElementNames;
|
||||
mEnteringFrom = mEnterTransitionCoordinator.getAcceptedNames();
|
||||
mEnteringTo = mEnterTransitionCoordinator.getMappedNames();
|
||||
}
|
||||
mExitingFrom = null;
|
||||
mExitingTo = null;
|
||||
mEnterActivityOptions = null;
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
if (mCalledActivityOptions != null) {
|
||||
mCalledActivityOptions.dispatchActivityStopped();
|
||||
mCalledActivityOptions = null;
|
||||
}
|
||||
if (mEnterTransitionCoordinator != null) {
|
||||
mEnterTransitionCoordinator.stop();
|
||||
mEnterTransitionCoordinator = null;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean startExitBackTransition(Activity activity) {
|
||||
if (mEnteringNames == null) {
|
||||
return false;
|
||||
} else {
|
||||
if (!mHasExited) {
|
||||
mHasExited = true;
|
||||
if (mEnterTransitionCoordinator != null) {
|
||||
mEnterTransitionCoordinator.stop();
|
||||
mEnterTransitionCoordinator = null;
|
||||
}
|
||||
ArrayMap<String, View> sharedElements = new ArrayMap<String, View>();
|
||||
activity.getWindow().getDecorView().findNamedViews(sharedElements);
|
||||
|
||||
ExitTransitionCoordinator exitCoordinator =
|
||||
new ExitTransitionCoordinator(activity, mEnteringNames, mEnteringFrom,
|
||||
mEnteringTo, true);
|
||||
exitCoordinator.startExit(activity.mResultCode, activity.mResultData);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void startExitOutTransition(Activity activity, Bundle options) {
|
||||
if (!activity.getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
|
||||
return;
|
||||
}
|
||||
mCalledActivityOptions = new ActivityOptions(options);
|
||||
if (mCalledActivityOptions.getAnimationType() == ActivityOptions.ANIM_SCENE_TRANSITION) {
|
||||
mExitingFrom = mCalledActivityOptions.getSharedElementNames();
|
||||
mExitingTo = mCalledActivityOptions.getLocalSharedElementNames();
|
||||
mCalledActivityOptions.dispatchStartExit();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -18,270 +18,414 @@ package android.app;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.ResultReceiver;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionManager;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroupOverlay;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
/**
|
||||
* This ActivityTransitionCoordinator is created by the Activity to manage
|
||||
* the enter scene and shared element transfer as well as Activity#finishWithTransition
|
||||
* exiting the Scene and transferring shared elements back to the called Activity.
|
||||
* the enter scene and shared element transfer into the Scene, either during
|
||||
* launch of an Activity or returning from a launched Activity.
|
||||
*/
|
||||
class EnterTransitionCoordinator extends ActivityTransitionCoordinator
|
||||
implements ViewTreeObserver.OnPreDrawListener {
|
||||
class EnterTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
private static final String TAG = "EnterTransitionCoordinator";
|
||||
|
||||
// The background fade in/out duration. 150ms is pretty quick, but not abrupt.
|
||||
private static final int FADE_BACKGROUND_DURATION_MS = 150;
|
||||
private static final long MAX_WAIT_MS = 1500;
|
||||
|
||||
/**
|
||||
* The shared element names sent by the ExitTransitionCoordinator and may be
|
||||
* shared when exiting back.
|
||||
*/
|
||||
private ArrayList<String> mEnteringSharedElementNames;
|
||||
|
||||
/**
|
||||
* The Activity that has created this coordinator. This is used solely to make the
|
||||
* Window translucent/opaque.
|
||||
*/
|
||||
private boolean mSharedElementTransitionStarted;
|
||||
private boolean mIsReturning;
|
||||
private Activity mActivity;
|
||||
private boolean mHasStopped;
|
||||
private Handler mHandler;
|
||||
private boolean mIsCanceled;
|
||||
|
||||
/**
|
||||
* True if the Window was opaque at the start and we should make it opaque again after
|
||||
* enter transitions have completed.
|
||||
*/
|
||||
private boolean mWasOpaque;
|
||||
|
||||
/**
|
||||
* During exit, is the background alpha == 0?
|
||||
*/
|
||||
private boolean mBackgroundFadedOut;
|
||||
|
||||
/**
|
||||
* During exit, has the shared element transition completed?
|
||||
*/
|
||||
private boolean mSharedElementTransitionComplete;
|
||||
|
||||
/**
|
||||
* Has the exit started? We don't want to accidentally exit multiple times. e.g. when
|
||||
* back is hit twice during the exit animation.
|
||||
*/
|
||||
private boolean mExitTransitionStarted;
|
||||
|
||||
/**
|
||||
* Has the exit transition ended?
|
||||
*/
|
||||
private boolean mExitTransitionComplete;
|
||||
|
||||
/**
|
||||
* We only want to make the Window transparent and set the background alpha once. After that,
|
||||
* the Activity won't want the same enter transition.
|
||||
*/
|
||||
private boolean mMadeReady;
|
||||
|
||||
/**
|
||||
* True if Window.hasFeature(Window.FEATURE_CONTENT_TRANSITIONS) -- this means that
|
||||
* enter and exit transitions should be active.
|
||||
*/
|
||||
private boolean mSupportsTransition;
|
||||
|
||||
/**
|
||||
* Background alpha animations may complete prior to receiving the callback for
|
||||
* onTranslucentConversionComplete. If so, we need to immediately call to make the Window
|
||||
* opaque.
|
||||
*/
|
||||
private boolean mMakeOpaque;
|
||||
|
||||
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver) {
|
||||
super(activity.getWindow());
|
||||
public EnterTransitionCoordinator(Activity activity, ResultReceiver resultReceiver,
|
||||
ArrayList<String> sharedElementNames,
|
||||
ArrayList<String> acceptedNames, ArrayList<String> mappedNames) {
|
||||
super(activity.getWindow(), sharedElementNames, acceptedNames, mappedNames,
|
||||
activity.mTransitionListener);
|
||||
mActivity = activity;
|
||||
setRemoteResultReceiver(resultReceiver);
|
||||
}
|
||||
|
||||
public void readyToEnter() {
|
||||
if (!mMadeReady) {
|
||||
mMadeReady = true;
|
||||
mSupportsTransition = getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS);
|
||||
if (mSupportsTransition) {
|
||||
Window window = getWindow();
|
||||
window.getDecorView().getViewTreeObserver().addOnPreDrawListener(this);
|
||||
mActivity.overridePendingTransition(0, 0);
|
||||
mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
|
||||
@Override
|
||||
public void onTranslucentConversionComplete(boolean drawComplete) {
|
||||
mWasOpaque = true;
|
||||
if (mMakeOpaque) {
|
||||
mActivity.convertFromTranslucent();
|
||||
}
|
||||
}
|
||||
}, null);
|
||||
Drawable background = getDecor().getBackground();
|
||||
if (background != null) {
|
||||
window.setBackgroundDrawable(null);
|
||||
background.setAlpha(0);
|
||||
window.setBackgroundDrawable(background);
|
||||
mIsReturning = acceptedNames != null;
|
||||
setResultReceiver(resultReceiver);
|
||||
prepareEnter();
|
||||
Bundle resultReceiverBundle = new Bundle();
|
||||
resultReceiverBundle.putParcelable(KEY_REMOTE_RECEIVER, this);
|
||||
mResultReceiver.send(MSG_SET_REMOTE_RECEIVER, resultReceiverBundle);
|
||||
if (mIsReturning) {
|
||||
mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
cancel();
|
||||
}
|
||||
}
|
||||
};
|
||||
mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
|
||||
mEnteringSharedElementNames = new ArrayList<String>();
|
||||
mEnteringSharedElementNames.addAll(sharedElementNames);
|
||||
super.onTakeSharedElements(sharedElementNames, state);
|
||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||
switch (resultCode) {
|
||||
case MSG_TAKE_SHARED_ELEMENTS:
|
||||
if (!mIsCanceled) {
|
||||
if (mHandler != null) {
|
||||
mHandler.removeMessages(MSG_CANCEL);
|
||||
}
|
||||
onTakeSharedElements(resultData);
|
||||
}
|
||||
break;
|
||||
case MSG_EXIT_TRANSITION_COMPLETE:
|
||||
if (!mIsCanceled) {
|
||||
if (!mSharedElementTransitionStarted) {
|
||||
send(resultCode, resultData);
|
||||
} else {
|
||||
onRemoteExitTransitionComplete();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case MSG_CANCEL:
|
||||
cancel();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sharedElementTransitionComplete(Bundle bundle) {
|
||||
notifySharedElementTransitionComplete(bundle);
|
||||
exitAfterSharedElementTransition();
|
||||
private void cancel() {
|
||||
if (!mIsCanceled) {
|
||||
mIsCanceled = true;
|
||||
if (getViewsTransition() == null) {
|
||||
setViewVisibility(mSharedElements, View.VISIBLE);
|
||||
} else {
|
||||
mTransitioningViews.addAll(mSharedElements);
|
||||
}
|
||||
mSharedElementNames.clear();
|
||||
mSharedElements.clear();
|
||||
mAllSharedElementNames.clear();
|
||||
onTakeSharedElements(null);
|
||||
onRemoteExitTransitionComplete();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getWindow().getDecorView().getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
setEnteringViews(readyEnteringViews());
|
||||
notifySetListener();
|
||||
onPrepareRestore();
|
||||
return false;
|
||||
public boolean isReturning() {
|
||||
return mIsReturning;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startExit() {
|
||||
if (!mExitTransitionStarted) {
|
||||
mExitTransitionStarted = true;
|
||||
startExitTransition(mEnteringSharedElementNames);
|
||||
protected void prepareEnter() {
|
||||
setViewVisibility(mSharedElements, View.INVISIBLE);
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(mTransitioningViews, View.INVISIBLE);
|
||||
}
|
||||
mActivity.overridePendingTransition(0, 0);
|
||||
if (!mIsReturning) {
|
||||
mActivity.convertToTranslucent(null, null);
|
||||
Drawable background = getDecor().getBackground();
|
||||
if (background != null) {
|
||||
getWindow().setBackgroundDrawable(null);
|
||||
background = background.mutate();
|
||||
background.setAlpha(0);
|
||||
getWindow().setBackgroundDrawable(background);
|
||||
}
|
||||
} else {
|
||||
mActivity = null; // all done with it now.
|
||||
}
|
||||
}
|
||||
|
||||
protected void onTakeSharedElements(Bundle sharedElementState) {
|
||||
setEpicenter();
|
||||
// Remove rejected shared elements
|
||||
ArrayList<String> rejectedNames = new ArrayList<String>(mAllSharedElementNames);
|
||||
rejectedNames.removeAll(mSharedElementNames);
|
||||
ArrayList<View> rejectedSnapshots = createSnapshots(sharedElementState, rejectedNames);
|
||||
mListener.handleRejectedSharedElements(rejectedSnapshots);
|
||||
startRejectedAnimations(rejectedSnapshots);
|
||||
|
||||
// Now start shared element transition
|
||||
ArrayList<View> sharedElementSnapshots = createSnapshots(sharedElementState,
|
||||
mSharedElementNames);
|
||||
setViewVisibility(mSharedElements, View.VISIBLE);
|
||||
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageViewState =
|
||||
setSharedElementState(sharedElementState, sharedElementSnapshots);
|
||||
|
||||
boolean startEnterTransition = allowOverlappingTransitions();
|
||||
boolean startSharedElementTransition = true;
|
||||
Transition transition = beginTransition(startEnterTransition, startSharedElementTransition);
|
||||
|
||||
if (startEnterTransition) {
|
||||
startEnterTransition(transition);
|
||||
}
|
||||
|
||||
setOriginalImageViewState(originalImageViewState);
|
||||
|
||||
if (mResultReceiver != null) {
|
||||
mResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
|
||||
}
|
||||
mResultReceiver = null; // all done sending messages.
|
||||
}
|
||||
|
||||
private Transition beginTransition(boolean startEnterTransition,
|
||||
boolean startSharedElementTransition) {
|
||||
Transition sharedElementTransition = null;
|
||||
if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
|
||||
sharedElementTransition = configureTransition(getSharedElementTransition());
|
||||
}
|
||||
Transition viewsTransition = null;
|
||||
if (startEnterTransition && !mTransitioningViews.isEmpty()) {
|
||||
viewsTransition = configureTransition(getViewsTransition());
|
||||
viewsTransition = addTargets(viewsTransition, mTransitioningViews);
|
||||
}
|
||||
|
||||
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
|
||||
if (transition != null) {
|
||||
TransitionManager.beginDelayedTransition(getDecor(), transition);
|
||||
if (startSharedElementTransition && !mSharedElementNames.isEmpty()) {
|
||||
mSharedElements.get(0).invalidate();
|
||||
} else if (startEnterTransition && !mTransitioningViews.isEmpty()) {
|
||||
mTransitioningViews.get(0).invalidate();
|
||||
}
|
||||
}
|
||||
return transition;
|
||||
}
|
||||
|
||||
private void startEnterTransition(Transition transition) {
|
||||
setViewVisibility(mTransitioningViews, View.VISIBLE);
|
||||
if (!mIsReturning) {
|
||||
Drawable background = getDecor().getBackground();
|
||||
if (background != null) {
|
||||
background = background.mutate();
|
||||
ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255);
|
||||
animator.setDuration(FADE_BACKGROUND_DURATION_MS);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
makeOpaque();
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
} else if (transition != null) {
|
||||
transition.addListener(new Transition.TransitionListenerAdapter() {
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
transition.removeListener(this);
|
||||
makeOpaque();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
makeOpaque();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mHasStopped = true;
|
||||
mActivity = null;
|
||||
mIsCanceled = true;
|
||||
mResultReceiver = null;
|
||||
}
|
||||
|
||||
private void makeOpaque() {
|
||||
if (!mHasStopped) {
|
||||
mActivity.convertFromTranslucent();
|
||||
mActivity = null;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean allowOverlappingTransitions() {
|
||||
return mIsReturning ? getWindow().getAllowExitTransitionOverlap()
|
||||
: getWindow().getAllowEnterTransitionOverlap();
|
||||
}
|
||||
|
||||
private void startRejectedAnimations(final ArrayList<View> rejectedSnapshots) {
|
||||
if (rejectedSnapshots == null || rejectedSnapshots.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
ViewGroupOverlay overlay = getDecor().getOverlay();
|
||||
ObjectAnimator animator = null;
|
||||
int numRejected = rejectedSnapshots.size();
|
||||
for (int i = 0; i < numRejected; i++) {
|
||||
View snapshot = rejectedSnapshots.get(i);
|
||||
overlay.add(snapshot);
|
||||
animator = ObjectAnimator.ofFloat(snapshot, View.ALPHA, 1, 0);
|
||||
animator.start();
|
||||
}
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
ViewGroupOverlay overlay = getDecor().getOverlay();
|
||||
int numRejected = rejectedSnapshots.size();
|
||||
for (int i = 0; i < numRejected; i++) {
|
||||
overlay.remove(rejectedSnapshots.get(i));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected void onRemoteExitTransitionComplete() {
|
||||
if (!allowOverlappingTransitions()) {
|
||||
boolean startEnterTransition = true;
|
||||
boolean startSharedElementTransition = false;
|
||||
Transition transition = beginTransition(startEnterTransition,
|
||||
startSharedElementTransition);
|
||||
startEnterTransition(transition);
|
||||
}
|
||||
}
|
||||
|
||||
private ArrayList<View> createSnapshots(Bundle state, Collection<String> names) {
|
||||
int numSharedElements = names.size();
|
||||
if (numSharedElements == 0) {
|
||||
return null;
|
||||
}
|
||||
ArrayList<View> snapshots = new ArrayList<View>(numSharedElements);
|
||||
Context context = getWindow().getContext();
|
||||
int[] parentLoc = new int[2];
|
||||
getDecor().getLocationOnScreen(parentLoc);
|
||||
for (String name: names) {
|
||||
Bundle sharedElementBundle = state.getBundle(name);
|
||||
if (sharedElementBundle != null) {
|
||||
Bitmap bitmap = sharedElementBundle.getParcelable(KEY_BITMAP);
|
||||
View snapshot = new View(context);
|
||||
snapshot.setId(com.android.internal.R.id.shared_element);
|
||||
Resources resources = getWindow().getContext().getResources();
|
||||
snapshot.setBackground(new BitmapDrawable(resources, bitmap));
|
||||
snapshot.setViewName(name);
|
||||
setSharedElementState(snapshot, name, state, parentLoc);
|
||||
snapshots.add(snapshot);
|
||||
}
|
||||
}
|
||||
return snapshots;
|
||||
}
|
||||
|
||||
private static void setSharedElementState(View view, String name, Bundle transitionArgs,
|
||||
int[] parentLoc) {
|
||||
Bundle sharedElementBundle = transitionArgs.getBundle(name);
|
||||
if (sharedElementBundle == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (view instanceof ImageView) {
|
||||
int scaleTypeInt = sharedElementBundle.getInt(KEY_SCALE_TYPE, -1);
|
||||
if (scaleTypeInt >= 0) {
|
||||
ImageView imageView = (ImageView) view;
|
||||
ImageView.ScaleType scaleType = SCALE_TYPE_VALUES[scaleTypeInt];
|
||||
imageView.setScaleType(scaleType);
|
||||
if (scaleType == ImageView.ScaleType.MATRIX) {
|
||||
float[] matrixValues = sharedElementBundle.getFloatArray(KEY_IMAGE_MATRIX);
|
||||
Matrix matrix = new Matrix();
|
||||
matrix.setValues(matrixValues);
|
||||
imageView.setImageMatrix(matrix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z);
|
||||
view.setTranslationZ(z);
|
||||
|
||||
int x = sharedElementBundle.getInt(KEY_SCREEN_X);
|
||||
int y = sharedElementBundle.getInt(KEY_SCREEN_Y);
|
||||
int width = sharedElementBundle.getInt(KEY_WIDTH);
|
||||
int height = sharedElementBundle.getInt(KEY_HEIGHT);
|
||||
|
||||
int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY);
|
||||
int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
|
||||
view.measure(widthSpec, heightSpec);
|
||||
|
||||
int left = x - parentLoc[0];
|
||||
int top = y - parentLoc[1];
|
||||
int right = left + width;
|
||||
int bottom = top + height;
|
||||
view.layout(left, top, right, bottom);
|
||||
}
|
||||
|
||||
private ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> setSharedElementState(
|
||||
Bundle sharedElementState, final ArrayList<View> snapshots) {
|
||||
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalImageState =
|
||||
new ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>>();
|
||||
if (sharedElementState != null) {
|
||||
int[] tempLoc = new int[2];
|
||||
for (int i = 0; i < mSharedElementNames.size(); i++) {
|
||||
View sharedElement = mSharedElements.get(i);
|
||||
String name = mSharedElementNames.get(i);
|
||||
Pair<ImageView.ScaleType, Matrix> originalState = getOldImageState(sharedElement,
|
||||
name, sharedElementState);
|
||||
if (originalState != null) {
|
||||
originalImageState.put((ImageView) sharedElement, originalState);
|
||||
}
|
||||
View parent = (View) sharedElement.getParent();
|
||||
parent.getLocationOnScreen(tempLoc);
|
||||
setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
|
||||
sharedElement.requestLayout();
|
||||
}
|
||||
}
|
||||
mListener.setSharedElementStart(mSharedElementNames, mSharedElements, snapshots);
|
||||
|
||||
getDecor().getViewTreeObserver().addOnPreDrawListener(
|
||||
new ViewTreeObserver.OnPreDrawListener() {
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
mListener.setSharedElementEnd(mSharedElementNames, mSharedElements,
|
||||
snapshots);
|
||||
mSharedElementTransitionStarted = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
);
|
||||
return originalImageState;
|
||||
}
|
||||
|
||||
private static Pair<ImageView.ScaleType, Matrix> getOldImageState(View view, String name,
|
||||
Bundle transitionArgs) {
|
||||
if (!(view instanceof ImageView)) {
|
||||
return null;
|
||||
}
|
||||
Bundle bundle = transitionArgs.getBundle(name);
|
||||
int scaleTypeInt = bundle.getInt(KEY_SCALE_TYPE, -1);
|
||||
if (scaleTypeInt < 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
ImageView imageView = (ImageView) view;
|
||||
ImageView.ScaleType originalScaleType = imageView.getScaleType();
|
||||
|
||||
Matrix originalMatrix = null;
|
||||
if (originalScaleType == ImageView.ScaleType.MATRIX) {
|
||||
originalMatrix = new Matrix(imageView.getImageMatrix());
|
||||
}
|
||||
|
||||
return Pair.create(originalScaleType, originalMatrix);
|
||||
}
|
||||
|
||||
private static void setOriginalImageViewState(
|
||||
ArrayMap<ImageView, Pair<ImageView.ScaleType, Matrix>> originalState) {
|
||||
for (int i = 0; i < originalState.size(); i++) {
|
||||
ImageView imageView = originalState.keyAt(i);
|
||||
Pair<ImageView.ScaleType, Matrix> state = originalState.valueAt(i);
|
||||
imageView.setScaleType(state.first);
|
||||
imageView.setImageMatrix(state.second);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transition getViewsTransition() {
|
||||
if (!mSupportsTransition) {
|
||||
return null;
|
||||
}
|
||||
return getWindow().getEnterTransition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transition getSharedElementTransition() {
|
||||
if (!mSupportsTransition) {
|
||||
return null;
|
||||
}
|
||||
return getWindow().getSharedElementEnterTransition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartEnterTransition(Transition transition, ArrayList<View> enteringViews) {
|
||||
Drawable background = getDecor().getBackground();
|
||||
if (background != null) {
|
||||
ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 255);
|
||||
animator.setDuration(FADE_BACKGROUND_DURATION_MS);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mMakeOpaque = true;
|
||||
if (mWasOpaque) {
|
||||
mActivity.convertFromTranslucent();
|
||||
}
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
} else if (mWasOpaque) {
|
||||
transition.addListener(new Transition.TransitionListenerAdapter() {
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
mMakeOpaque = true;
|
||||
mActivity.convertFromTranslucent();
|
||||
}
|
||||
});
|
||||
}
|
||||
super.onStartEnterTransition(transition, enteringViews);
|
||||
}
|
||||
|
||||
public ArrayList<View> readyEnteringViews() {
|
||||
ArrayList<View> enteringViews = new ArrayList<View>();
|
||||
getDecor().captureTransitioningViews(enteringViews);
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(enteringViews, View.INVISIBLE);
|
||||
}
|
||||
return enteringViews;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startExitTransition(ArrayList<String> sharedElements) {
|
||||
mMakeOpaque = false;
|
||||
notifyPrepareRestore();
|
||||
|
||||
if (getDecor().getBackground() == null) {
|
||||
ColorDrawable black = new ColorDrawable(0xFF000000);
|
||||
getWindow().setBackgroundDrawable(black);
|
||||
}
|
||||
if (mWasOpaque) {
|
||||
mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
|
||||
@Override
|
||||
public void onTranslucentConversionComplete(boolean drawComplete) {
|
||||
fadeOutBackground();
|
||||
}
|
||||
}, null);
|
||||
} else {
|
||||
fadeOutBackground();
|
||||
}
|
||||
|
||||
super.startExitTransition(sharedElements);
|
||||
}
|
||||
|
||||
private void fadeOutBackground() {
|
||||
ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(),
|
||||
"alpha", 0);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mBackgroundFadedOut = true;
|
||||
if (mSharedElementTransitionComplete) {
|
||||
EnterTransitionCoordinator.super.onSharedElementTransitionEnd();
|
||||
}
|
||||
}
|
||||
});
|
||||
animator.setDuration(FADE_BACKGROUND_DURATION_MS);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onExitTransitionEnd() {
|
||||
mExitTransitionComplete = true;
|
||||
exitAfterSharedElementTransition();
|
||||
super.onExitTransitionEnd();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSharedElementTransitionEnd() {
|
||||
mSharedElementTransitionComplete = true;
|
||||
if (mBackgroundFadedOut) {
|
||||
super.onSharedElementTransitionEnd();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowOverlappingTransitions() {
|
||||
return getWindow().getAllowEnterTransitionOverlap();
|
||||
}
|
||||
|
||||
private void exitAfterSharedElementTransition() {
|
||||
if (mSharedElementTransitionComplete && mExitTransitionComplete && mBackgroundFadedOut) {
|
||||
mActivity.finish();
|
||||
if (mSupportsTransition) {
|
||||
mActivity.overridePendingTransition(0, 0);
|
||||
}
|
||||
notifyExitTransitionComplete();
|
||||
clearConnections();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,11 +15,20 @@
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.transition.Transition;
|
||||
import android.util.Pair;
|
||||
import android.transition.TransitionManager;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
@@ -30,142 +39,245 @@ import java.util.ArrayList;
|
||||
*/
|
||||
class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
private static final String TAG = "ExitTransitionCoordinator";
|
||||
private static final long MAX_WAIT_MS = 1500;
|
||||
|
||||
/**
|
||||
* The Views that have exited and need to be restored to VISIBLE when returning to the
|
||||
* normal state.
|
||||
*/
|
||||
private ArrayList<View> mTransitioningViews;
|
||||
|
||||
/**
|
||||
* Has the exit started? We don't want to accidentally exit multiple times.
|
||||
*/
|
||||
private boolean mExitStarted;
|
||||
|
||||
/**
|
||||
* Has the called Activity's ResultReceiver been set?
|
||||
*/
|
||||
private boolean mIsResultReceiverSet;
|
||||
|
||||
/**
|
||||
* Has the exit transition completed? If so, we can notify as soon as the ResultReceiver
|
||||
* has been set.
|
||||
*/
|
||||
private boolean mExitComplete;
|
||||
|
||||
/**
|
||||
* Has the shared element transition completed? If so, we can notify as soon as the
|
||||
* ResultReceiver has been set.
|
||||
*/
|
||||
private Bundle mSharedElements;
|
||||
private Bundle mSharedElementBundle;
|
||||
|
||||
/**
|
||||
* Has the shared element transition completed?
|
||||
*/
|
||||
private boolean mSharedElementsComplete;
|
||||
private boolean mExitNotified;
|
||||
|
||||
public ExitTransitionCoordinator(Window window,
|
||||
ActivityOptions.ActivityTransitionListener listener) {
|
||||
super(window);
|
||||
setActivityTransitionListener(listener);
|
||||
private boolean mSharedElementNotified;
|
||||
|
||||
private Activity mActivity;
|
||||
|
||||
private boolean mIsBackgroundReady;
|
||||
|
||||
private boolean mIsReturning;
|
||||
|
||||
private boolean mIsCanceled;
|
||||
|
||||
private Handler mHandler;
|
||||
|
||||
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
|
||||
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
|
||||
super(activity.getWindow(), names, accepted, mapped, activity.mTransitionListener);
|
||||
mIsReturning = isReturning;
|
||||
mIsBackgroundReady = !mIsReturning;
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSetResultReceiver() {
|
||||
mIsResultReceiverSet = true;
|
||||
notifyCompletions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareRestore() {
|
||||
makeTransitioningViewsInvisible();
|
||||
setEnteringViews(mTransitioningViews);
|
||||
mTransitioningViews = null;
|
||||
super.onPrepareRestore();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onTakeSharedElements(ArrayList<String> sharedElementNames, Bundle state) {
|
||||
super.onTakeSharedElements(sharedElementNames, state);
|
||||
clearConnections();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityStopped() {
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(mTransitioningViews, View.VISIBLE);
|
||||
}
|
||||
super.onActivityStopped();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void sharedElementTransitionComplete(Bundle bundle) {
|
||||
mSharedElements = bundle;
|
||||
mSharedElementsComplete = true;
|
||||
notifyCompletions();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onExitTransitionEnd() {
|
||||
mExitComplete = true;
|
||||
notifyCompletions();
|
||||
super.onExitTransitionEnd();
|
||||
}
|
||||
|
||||
private void notifyCompletions() {
|
||||
if (mIsResultReceiverSet && mSharedElementsComplete) {
|
||||
if (mSharedElements != null) {
|
||||
notifySharedElementTransitionComplete(mSharedElements);
|
||||
mSharedElements = null;
|
||||
}
|
||||
if (mExitComplete) {
|
||||
notifyExitTransitionComplete();
|
||||
}
|
||||
protected void onReceiveResult(int resultCode, Bundle resultData) {
|
||||
switch (resultCode) {
|
||||
case MSG_SET_REMOTE_RECEIVER:
|
||||
mResultReceiver = resultData.getParcelable(KEY_REMOTE_RECEIVER);
|
||||
if (mIsCanceled) {
|
||||
mResultReceiver.send(MSG_CANCEL, null);
|
||||
mResultReceiver = null;
|
||||
} else {
|
||||
if (mHandler != null) {
|
||||
mHandler.removeMessages(MSG_CANCEL);
|
||||
}
|
||||
notifyComplete();
|
||||
}
|
||||
break;
|
||||
case MSG_HIDE_SHARED_ELEMENTS:
|
||||
if (!mIsCanceled) {
|
||||
hideSharedElements();
|
||||
}
|
||||
break;
|
||||
case MSG_START_EXIT_TRANSITION:
|
||||
startExit();
|
||||
break;
|
||||
case MSG_ACTIVITY_STOPPED:
|
||||
setViewVisibility(mTransitioningViews, View.VISIBLE);
|
||||
setViewVisibility(mSharedElements, View.VISIBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
private void hideSharedElements() {
|
||||
setViewVisibility(mSharedElements, View.INVISIBLE);
|
||||
}
|
||||
|
||||
public void startExit() {
|
||||
if (!mExitStarted) {
|
||||
mExitStarted = true;
|
||||
setSharedElements();
|
||||
startExitTransition(getSharedElementNames());
|
||||
beginTransition();
|
||||
setViewVisibility(mTransitioningViews, View.INVISIBLE);
|
||||
}
|
||||
|
||||
public void startExit(int resultCode, Intent data) {
|
||||
mHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
mIsCanceled = true;
|
||||
mActivity.finish();
|
||||
mActivity = null;
|
||||
}
|
||||
};
|
||||
mHandler.sendEmptyMessageDelayed(MSG_CANCEL, MAX_WAIT_MS);
|
||||
if (getDecor().getBackground() == null) {
|
||||
ColorDrawable black = new ColorDrawable(0xFF000000);
|
||||
black.setAlpha(0);
|
||||
getWindow().setBackgroundDrawable(black);
|
||||
black.setAlpha(255);
|
||||
}
|
||||
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
|
||||
mAllSharedElementNames, resultCode, data);
|
||||
mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
|
||||
@Override
|
||||
public void onTranslucentConversionComplete(boolean drawComplete) {
|
||||
if (!mIsCanceled) {
|
||||
fadeOutBackground();
|
||||
}
|
||||
}
|
||||
}, options);
|
||||
startExit();
|
||||
}
|
||||
|
||||
private void fadeOutBackground() {
|
||||
ObjectAnimator animator = ObjectAnimator.ofInt(getDecor().getBackground(),
|
||||
"alpha", 0);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
mIsBackgroundReady = true;
|
||||
notifyComplete();
|
||||
}
|
||||
});
|
||||
animator.setDuration(FADE_BACKGROUND_DURATION_MS);
|
||||
animator.start();
|
||||
}
|
||||
|
||||
private void beginTransition() {
|
||||
Transition sharedElementTransition = configureTransition(getSharedElementTransition());
|
||||
Transition viewsTransition = configureTransition(getViewsTransition());
|
||||
viewsTransition = addTargets(viewsTransition, mTransitioningViews);
|
||||
if (sharedElementTransition == null) {
|
||||
sharedElementTransitionComplete();
|
||||
} else {
|
||||
sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
sharedElementTransitionComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
if (viewsTransition == null) {
|
||||
exitTransitionComplete();
|
||||
} else {
|
||||
viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
exitTransitionComplete();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
|
||||
TransitionManager.beginDelayedTransition(getDecor(), transition);
|
||||
}
|
||||
|
||||
private void exitTransitionComplete() {
|
||||
mExitComplete = true;
|
||||
notifyComplete();
|
||||
}
|
||||
|
||||
protected boolean isReadyToNotify() {
|
||||
return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady;
|
||||
}
|
||||
|
||||
private void sharedElementTransitionComplete() {
|
||||
Bundle bundle = new Bundle();
|
||||
int[] tempLoc = new int[2];
|
||||
for (int i = 0; i < mSharedElementNames.size(); i++) {
|
||||
View sharedElement = mSharedElements.get(i);
|
||||
String name = mSharedElementNames.get(i);
|
||||
captureSharedElementState(sharedElement, name, bundle, tempLoc);
|
||||
}
|
||||
mSharedElementBundle = bundle;
|
||||
notifyComplete();
|
||||
}
|
||||
|
||||
protected void notifyComplete() {
|
||||
if (isReadyToNotify()) {
|
||||
if (!mSharedElementNotified) {
|
||||
mSharedElementNotified = true;
|
||||
mResultReceiver.send(MSG_TAKE_SHARED_ELEMENTS, mSharedElementBundle);
|
||||
}
|
||||
if (!mExitNotified && mExitComplete) {
|
||||
mExitNotified = true;
|
||||
mResultReceiver.send(MSG_EXIT_TRANSITION_COMPLETE, null);
|
||||
mResultReceiver = null; // done talking
|
||||
if (mIsReturning) {
|
||||
mActivity.finish();
|
||||
mActivity.overridePendingTransition(0, 0);
|
||||
}
|
||||
mActivity = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures placement information for Views with a shared element name for
|
||||
* Activity Transitions.
|
||||
*
|
||||
* @param view The View to capture the placement information for.
|
||||
* @param name The shared element name in the target Activity to apply the placement
|
||||
* information for.
|
||||
* @param transitionArgs Bundle to store shared element placement information.
|
||||
* @param tempLoc A temporary int[2] for capturing the current location of views.
|
||||
*/
|
||||
private static void captureSharedElementState(View view, String name, Bundle transitionArgs,
|
||||
int[] tempLoc) {
|
||||
Bundle sharedElementBundle = new Bundle();
|
||||
view.getLocationOnScreen(tempLoc);
|
||||
float scaleX = view.getScaleX();
|
||||
sharedElementBundle.putInt(KEY_SCREEN_X, tempLoc[0]);
|
||||
int width = Math.round(view.getWidth() * scaleX);
|
||||
sharedElementBundle.putInt(KEY_WIDTH, width);
|
||||
|
||||
float scaleY = view.getScaleY();
|
||||
sharedElementBundle.putInt(KEY_SCREEN_Y, tempLoc[1]);
|
||||
int height = Math.round(view.getHeight() * scaleY);
|
||||
sharedElementBundle.putInt(KEY_HEIGHT, height);
|
||||
|
||||
sharedElementBundle.putFloat(KEY_TRANSLATION_Z, view.getTranslationZ());
|
||||
|
||||
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||
Canvas canvas = new Canvas(bitmap);
|
||||
view.draw(canvas);
|
||||
sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);
|
||||
|
||||
if (view instanceof ImageView) {
|
||||
ImageView imageView = (ImageView) view;
|
||||
int scaleTypeInt = scaleTypeToInt(imageView.getScaleType());
|
||||
sharedElementBundle.putInt(KEY_SCALE_TYPE, scaleTypeInt);
|
||||
if (imageView.getScaleType() == ImageView.ScaleType.MATRIX) {
|
||||
float[] matrix = new float[9];
|
||||
imageView.getImageMatrix().getValues(matrix);
|
||||
sharedElementBundle.putFloatArray(KEY_IMAGE_MATRIX, matrix);
|
||||
}
|
||||
}
|
||||
|
||||
transitionArgs.putBundle(name, sharedElementBundle);
|
||||
}
|
||||
|
||||
private static int scaleTypeToInt(ImageView.ScaleType scaleType) {
|
||||
for (int i = 0; i < SCALE_TYPE_VALUES.length; i++) {
|
||||
if (scaleType == SCALE_TYPE_VALUES[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transition getViewsTransition() {
|
||||
if (!getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
|
||||
return null;
|
||||
}
|
||||
return getWindow().getExitTransition();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Transition getSharedElementTransition() {
|
||||
if (!getWindow().hasFeature(Window.FEATURE_CONTENT_TRANSITIONS)) {
|
||||
return null;
|
||||
}
|
||||
return getWindow().getSharedElementExitTransition();
|
||||
}
|
||||
|
||||
private void makeTransitioningViewsInvisible() {
|
||||
if (getViewsTransition() != null) {
|
||||
setViewVisibility(mTransitioningViews, View.INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStartExitTransition(ArrayList<View> exitingViews) {
|
||||
mTransitioningViews = new ArrayList<View>();
|
||||
if (exitingViews != null) {
|
||||
mTransitioningViews.addAll(exitingViews);
|
||||
}
|
||||
mTransitioningViews.addAll(getSharedElements());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean allowOverlappingTransitions() {
|
||||
return getWindow().getAllowExitTransitionOverlap();
|
||||
}
|
||||
}
|
||||
|
||||
87
core/java/android/app/SharedElementListener.java
Normal file
87
core/java/android/app/SharedElementListener.java
Normal file
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright (C) 2014 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package android.app;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Listener provided in
|
||||
* {@link Activity#setSharedElementListener(SharedElementListener)}
|
||||
* to monitor the Activity transitions. The events can be used to customize or override Activity
|
||||
* Transition behavior.
|
||||
*/
|
||||
public class SharedElementListener {
|
||||
/**
|
||||
* Called to allow the listener to customize the start state of the shared element for
|
||||
* the shared element entering transition. By default, the shared element is placed in
|
||||
* the position and with the size of the shared element in the calling Activity or Fragment.
|
||||
*
|
||||
* @param sharedElementNames The names of the shared elements that were accepted into
|
||||
* the View hierarchy.
|
||||
* @param sharedElements The shared elements that are part of the View hierarchy.
|
||||
* @param sharedElementSnapshots The Views containing snap shots of the shared element
|
||||
* from the launching Window. These elements will not
|
||||
* be part of the scene, but will be positioned relative
|
||||
* to the Window decor View.
|
||||
*/
|
||||
public void setSharedElementStart(List<String> sharedElementNames,
|
||||
List<View> sharedElements, List<View> sharedElementSnapshots) {}
|
||||
|
||||
/**
|
||||
* Called to allow the listener to customize the end state of the shared element for
|
||||
* the shared element entering transition.
|
||||
*
|
||||
* @param sharedElementNames The names of the shared elements that were accepted into
|
||||
* the View hierarchy.
|
||||
* @param sharedElements The shared elements that are part of the View hierarchy.
|
||||
* @param sharedElementSnapshots The Views containing snap shots of the shared element
|
||||
* from the launching Window. These elements will not
|
||||
* be part of the scene, but will be positioned relative
|
||||
* to the Window decor View.
|
||||
*/
|
||||
public void setSharedElementEnd(List<String> sharedElementNames,
|
||||
List<View> sharedElements, List<View> sharedElementSnapshots) {}
|
||||
|
||||
/**
|
||||
* If nothing is done, all shared elements that were not accepted by
|
||||
* {@link #remapSharedElements(java.util.List, java.util.Map)} will be Transitioned
|
||||
* out of the entering scene automatically. Any elements removed from
|
||||
* rejectedSharedElements must be handled by the ActivityTransitionListener.
|
||||
* <p>Views in rejectedSharedElements will have their position and size set to the
|
||||
* position of the calling shared element, relative to the Window decor View. This
|
||||
* view may be safely added to the decor View's overlay to remain in position.</p>
|
||||
*
|
||||
* @param rejectedSharedElements Views containing visual information of shared elements
|
||||
* that are not part of the entering scene. These Views
|
||||
* are positioned relative to the Window decor View. A
|
||||
* View removed from this list will not be transitioned
|
||||
* automatically.
|
||||
*/
|
||||
public void handleRejectedSharedElements(List<View> rejectedSharedElements) {}
|
||||
|
||||
/**
|
||||
* Lets the ActivityTransitionListener adjust the mapping of shared element names to
|
||||
* Views.
|
||||
* @param names The names of all shared elements transferred from the calling Activity
|
||||
* to the started Activity.
|
||||
* @param sharedElements The mapping of shared element names to Views. The best guess
|
||||
* will be filled into sharedElements based on the View names.
|
||||
*/
|
||||
public void remapSharedElements(List<String> names, Map<String, View> sharedElements) {}
|
||||
}
|
||||
@@ -2318,8 +2318,8 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager
|
||||
* in Activity transitions. If false, the ViewGroup won't transition,
|
||||
* only its children. If true, the entire ViewGroup will transition
|
||||
* together.
|
||||
* @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
|
||||
* android.app.ActivityOptions.ActivityTransitionListener)
|
||||
* @see android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
|
||||
* android.util.Pair[])
|
||||
*/
|
||||
public void setTransitionGroup(boolean isTransitionGroup) {
|
||||
mGroupFlags |= FLAG_IS_TRANSITION_GROUP_SET;
|
||||
|
||||
@@ -2477,7 +2477,7 @@
|
||||
when doing an Activity transition. Typically, the elements inside a
|
||||
ViewGroup are each transitioned from the scene individually. The default
|
||||
for a ViewGroup is false unless it has a background. See
|
||||
{@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.view.Window,
|
||||
{@link android.app.ActivityOptions#makeSceneTransitionAnimation(android.app.Activity,
|
||||
android.view.View, String)} for more information. Corresponds to
|
||||
{@link android.view.ViewGroup#setTransitionGroup(boolean)}.-->
|
||||
<attr name="transitionGroup" format="boolean" />
|
||||
|
||||
Reference in New Issue
Block a user