Merge "Fixing support for shared element transitions from a window"
This commit is contained in:
@@ -2590,7 +2590,7 @@ public class Activity extends ContextThemeWrapper
|
||||
protected void onStop() {
|
||||
if (DEBUG_LIFECYCLE) Slog.v(TAG, "onStop " + this);
|
||||
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(false);
|
||||
mActivityTransitionState.onStop();
|
||||
mActivityTransitionState.onStop(this);
|
||||
dispatchActivityStopped();
|
||||
mTranslucentCallback = null;
|
||||
mCalled = true;
|
||||
|
||||
@@ -27,6 +27,8 @@ import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.annotation.RequiresPermission;
|
||||
import android.annotation.TestApi;
|
||||
import android.app.ExitTransitionCoordinator.ActivityExitTransitionCallbacks;
|
||||
import android.app.ExitTransitionCoordinator.ExitTransitionCallbacks;
|
||||
import android.compat.annotation.UnsupportedAppUsage;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
@@ -44,8 +46,6 @@ import android.os.Parcelable;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ResultReceiver;
|
||||
import android.os.UserHandle;
|
||||
import android.transition.Transition;
|
||||
import android.transition.TransitionListenerAdapter;
|
||||
import android.transition.TransitionManager;
|
||||
import android.util.Pair;
|
||||
import android.util.Slog;
|
||||
@@ -806,8 +806,11 @@ public class ActivityOptions {
|
||||
public static ActivityOptions makeSceneTransitionAnimation(Activity activity,
|
||||
Pair<View, String>... sharedElements) {
|
||||
ActivityOptions opts = new ActivityOptions();
|
||||
makeSceneTransitionAnimation(activity, activity.getWindow(), opts,
|
||||
activity.mExitTransitionListener, sharedElements);
|
||||
ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
|
||||
new ActivityExitTransitionCallbacks(activity), activity.mExitTransitionListener,
|
||||
activity.getWindow(), opts, sharedElements);
|
||||
opts.mExitCoordinatorIndex =
|
||||
activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -823,25 +826,19 @@ public class ActivityOptions {
|
||||
* @hide
|
||||
*/
|
||||
@SafeVarargs
|
||||
public static ActivityOptions startSharedElementAnimation(Window window,
|
||||
public static Pair<ActivityOptions, ExitTransitionCoordinator> startSharedElementAnimation(
|
||||
Window window, ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback,
|
||||
Pair<View, String>... sharedElements) {
|
||||
ActivityOptions opts = new ActivityOptions();
|
||||
final View decorView = window.getDecorView();
|
||||
if (decorView == null) {
|
||||
return opts;
|
||||
}
|
||||
final ExitTransitionCoordinator exit =
|
||||
makeSceneTransitionAnimation(null, window, opts, null, sharedElements);
|
||||
if (exit != null) {
|
||||
HideWindowListener listener = new HideWindowListener(window, exit);
|
||||
exit.setHideSharedElementsCallback(listener);
|
||||
exit.startExit();
|
||||
}
|
||||
return opts;
|
||||
ExitTransitionCoordinator exit = makeSceneTransitionAnimation(
|
||||
exitCallbacks, callback, window, opts, sharedElements);
|
||||
opts.mExitCoordinatorIndex = -1;
|
||||
return Pair.create(opts, exit);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method should be called when the {@link #startSharedElementAnimation(Window, Pair[])}
|
||||
* This method should be called when the
|
||||
* {@link #startSharedElementAnimation(Window, ExitTransitionCallbacks, Pair[])}
|
||||
* animation must be stopped and the Views reset. This can happen if there was an error
|
||||
* from startActivity or a springboard activity and the animation should stop and reset.
|
||||
*
|
||||
@@ -864,9 +861,9 @@ public class ActivityOptions {
|
||||
}
|
||||
}
|
||||
|
||||
static ExitTransitionCoordinator makeSceneTransitionAnimation(Activity activity, Window window,
|
||||
ActivityOptions opts, SharedElementCallback callback,
|
||||
Pair<View, String>[] sharedElements) {
|
||||
static ExitTransitionCoordinator makeSceneTransitionAnimation(
|
||||
ExitTransitionCallbacks exitCallbacks, SharedElementCallback callback, Window window,
|
||||
ActivityOptions opts, Pair<View, String>[] sharedElements) {
|
||||
if (!window.hasFeature(Window.FEATURE_ACTIVITY_TRANSITIONS)) {
|
||||
opts.mAnimationType = ANIM_DEFAULT;
|
||||
return null;
|
||||
@@ -892,17 +889,11 @@ public class ActivityOptions {
|
||||
}
|
||||
}
|
||||
|
||||
ExitTransitionCoordinator exit = new ExitTransitionCoordinator(activity, window,
|
||||
ExitTransitionCoordinator exit = new ExitTransitionCoordinator(exitCallbacks, window,
|
||||
callback, names, names, views, false);
|
||||
opts.mTransitionReceiver = exit;
|
||||
opts.mSharedElementNames = names;
|
||||
opts.mIsReturning = (activity == null);
|
||||
if (activity == null) {
|
||||
opts.mExitCoordinatorIndex = -1;
|
||||
} else {
|
||||
opts.mExitCoordinatorIndex =
|
||||
activity.mActivityTransitionState.addExitTransitionCoordinator(exit);
|
||||
}
|
||||
opts.mIsReturning = false;
|
||||
return exit;
|
||||
}
|
||||
|
||||
@@ -928,8 +919,12 @@ public class ActivityOptions {
|
||||
opts.mIsReturning = true;
|
||||
opts.mResultCode = resultCode;
|
||||
opts.mResultData = resultData;
|
||||
opts.mExitCoordinatorIndex =
|
||||
activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
|
||||
if (activity == null) {
|
||||
opts.mExitCoordinatorIndex = -1;
|
||||
} else {
|
||||
opts.mExitCoordinatorIndex =
|
||||
activity.mActivityTransitionState.addExitTransitionCoordinator(exitCoordinator);
|
||||
}
|
||||
return opts;
|
||||
}
|
||||
|
||||
@@ -1868,67 +1863,6 @@ public class ActivityOptions {
|
||||
+ mStartY + ", mWidth=" + mWidth + ", mHeight=" + mHeight;
|
||||
}
|
||||
|
||||
private static class HideWindowListener extends TransitionListenerAdapter
|
||||
implements ExitTransitionCoordinator.HideSharedElementsCallback {
|
||||
private final Window mWindow;
|
||||
private final ExitTransitionCoordinator mExit;
|
||||
private final boolean mWaitingForTransition;
|
||||
private boolean mTransitionEnded;
|
||||
private boolean mSharedElementHidden;
|
||||
private ArrayList<View> mSharedElements;
|
||||
|
||||
public HideWindowListener(Window window, ExitTransitionCoordinator exit) {
|
||||
mWindow = window;
|
||||
mExit = exit;
|
||||
mSharedElements = new ArrayList<>(exit.mSharedElements);
|
||||
Transition transition = mWindow.getExitTransition();
|
||||
if (transition != null) {
|
||||
transition.addListener(this);
|
||||
mWaitingForTransition = true;
|
||||
} else {
|
||||
mWaitingForTransition = false;
|
||||
}
|
||||
View decorView = mWindow.getDecorView();
|
||||
if (decorView != null) {
|
||||
if (decorView.getTag(com.android.internal.R.id.cross_task_transition) != null) {
|
||||
throw new IllegalStateException(
|
||||
"Cannot start a transition while one is running");
|
||||
}
|
||||
decorView.setTagInternal(com.android.internal.R.id.cross_task_transition, exit);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionEnd(Transition transition) {
|
||||
mTransitionEnded = true;
|
||||
hideWhenDone();
|
||||
transition.removeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hideSharedElements() {
|
||||
mSharedElementHidden = true;
|
||||
hideWhenDone();
|
||||
}
|
||||
|
||||
private void hideWhenDone() {
|
||||
if (mSharedElementHidden && (!mWaitingForTransition || mTransitionEnded)) {
|
||||
mExit.resetViews();
|
||||
int numSharedElements = mSharedElements.size();
|
||||
for (int i = 0; i < numSharedElements; i++) {
|
||||
View view = mSharedElements.get(i);
|
||||
view.requestLayout();
|
||||
}
|
||||
View decorView = mWindow.getDecorView();
|
||||
if (decorView != null) {
|
||||
decorView.setTagInternal(
|
||||
com.android.internal.R.id.cross_task_transition, null);
|
||||
decorView.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The information about the source of activity launch. E.g. describe an activity is launched
|
||||
* from launcher by receiving a motion event with a timestamp.
|
||||
|
||||
@@ -247,14 +247,14 @@ class ActivityTransitionState {
|
||||
mEnterActivityOptions = null;
|
||||
}
|
||||
|
||||
public void onStop() {
|
||||
public void onStop(Activity activity) {
|
||||
restoreExitedViews();
|
||||
if (mEnterTransitionCoordinator != null) {
|
||||
mEnterTransitionCoordinator.stop();
|
||||
mEnterTransitionCoordinator = null;
|
||||
}
|
||||
if (mReturnExitCoordinator != null) {
|
||||
mReturnExitCoordinator.stop();
|
||||
mReturnExitCoordinator.stop(activity);
|
||||
mReturnExitCoordinator = null;
|
||||
}
|
||||
}
|
||||
@@ -331,7 +331,8 @@ class ActivityTransitionState {
|
||||
}
|
||||
}
|
||||
|
||||
mReturnExitCoordinator = new ExitTransitionCoordinator(activity,
|
||||
mReturnExitCoordinator = new ExitTransitionCoordinator(
|
||||
new ExitTransitionCoordinator.ActivityExitTransitionCallbacks(activity),
|
||||
activity.getWindow(), activity.mEnterTransitionListener, pendingExitNames,
|
||||
null, null, true);
|
||||
if (enterViewsTransition != null && decor != null) {
|
||||
@@ -341,12 +342,11 @@ class ActivityTransitionState {
|
||||
final ViewGroup finalDecor = decor;
|
||||
OneShotPreDrawListener.add(decor, () -> {
|
||||
if (mReturnExitCoordinator != null) {
|
||||
mReturnExitCoordinator.startExit(activity.mResultCode,
|
||||
activity.mResultData);
|
||||
mReturnExitCoordinator.startExit(activity);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
mReturnExitCoordinator.startExit(activity.mResultCode, activity.mResultData);
|
||||
mReturnExitCoordinator.startExit(activity);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -18,6 +18,7 @@ package android.app;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.NonNull;
|
||||
import android.app.SharedElementCallback.OnSharedElementsReadyListener;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
@@ -45,15 +46,17 @@ import java.util.ArrayList;
|
||||
* This ActivityTransitionCoordinator is created in ActivityOptions#makeSceneTransitionAnimation
|
||||
* to govern the exit of the Scene and the shared elements when calling an Activity as well as
|
||||
* the reentry of the Scene when coming back from the called Activity.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
public class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
private static final String TAG = "ExitTransitionCoordinator";
|
||||
static long sMaxWaitMillis = 1000;
|
||||
|
||||
private Bundle mSharedElementBundle;
|
||||
private boolean mExitNotified;
|
||||
private boolean mSharedElementNotified;
|
||||
private Activity mActivity;
|
||||
private ExitTransitionCallbacks mExitCallbacks;
|
||||
private boolean mIsBackgroundReady;
|
||||
private boolean mIsCanceled;
|
||||
private Handler mHandler;
|
||||
@@ -62,20 +65,15 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
private Bundle mExitSharedElementBundle;
|
||||
private boolean mIsExitStarted;
|
||||
private boolean mSharedElementsHidden;
|
||||
private HideSharedElementsCallback mHideSharedElementsCallback;
|
||||
|
||||
public ExitTransitionCoordinator(Activity activity, Window window,
|
||||
SharedElementCallback listener, ArrayList<String> names,
|
||||
public ExitTransitionCoordinator(ExitTransitionCallbacks exitCallbacks,
|
||||
Window window, SharedElementCallback listener, ArrayList<String> names,
|
||||
ArrayList<String> accepted, ArrayList<View> mapped, boolean isReturning) {
|
||||
super(window, names, listener, isReturning);
|
||||
viewsReady(mapSharedElements(accepted, mapped));
|
||||
stripOffscreenViews();
|
||||
mIsBackgroundReady = !isReturning;
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
void setHideSharedElementsCallback(HideSharedElementsCallback callback) {
|
||||
mHideSharedElementsCallback = callback;
|
||||
mExitCallbacks = exitCallbacks;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -190,8 +188,8 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
|
||||
private void hideSharedElements() {
|
||||
moveSharedElementsFromOverlay();
|
||||
if (mHideSharedElementsCallback != null) {
|
||||
mHideSharedElementsCallback.hideSharedElements();
|
||||
if (mExitCallbacks != null) {
|
||||
mExitCallbacks.hideSharedElements();
|
||||
}
|
||||
if (!mIsHidden) {
|
||||
hideViews(mSharedElements);
|
||||
@@ -210,20 +208,16 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
decorView.suppressLayout(true);
|
||||
}
|
||||
moveSharedElementsToOverlay();
|
||||
startTransition(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (mActivity != null) {
|
||||
beginTransitions();
|
||||
} else {
|
||||
startExitTransition();
|
||||
}
|
||||
}
|
||||
});
|
||||
startTransition(this::beginTransitions);
|
||||
}
|
||||
}
|
||||
|
||||
public void startExit(int resultCode, Intent data) {
|
||||
/**
|
||||
* Starts the exit animation and sends back the activity result
|
||||
*/
|
||||
public void startExit(Activity activity) {
|
||||
int resultCode = activity.mResultCode;
|
||||
Intent data = activity.mResultData;
|
||||
if (!mIsExitStarted) {
|
||||
mIsExitStarted = true;
|
||||
pauseInput();
|
||||
@@ -247,9 +241,9 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
.getApplicationInfo().targetSdkVersion >= VERSION_CODES.M;
|
||||
ArrayList<String> sharedElementNames = targetsM ? mSharedElementNames :
|
||||
mAllSharedElementNames;
|
||||
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(mActivity, this,
|
||||
ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, this,
|
||||
sharedElementNames, resultCode, data);
|
||||
mActivity.convertToTranslucent(new Activity.TranslucentConversionListener() {
|
||||
activity.convertToTranslucent(new Activity.TranslucentConversionListener() {
|
||||
@Override
|
||||
public void onTranslucentConversionComplete(boolean drawComplete) {
|
||||
if (!mIsCanceled) {
|
||||
@@ -257,21 +251,19 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
}
|
||||
}
|
||||
}, options);
|
||||
startTransition(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
startExitTransition();
|
||||
}
|
||||
});
|
||||
startTransition(this::startExitTransition);
|
||||
}
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
if (mIsReturning && mActivity != null) {
|
||||
/**
|
||||
* Called from {@link Activity#onStop()}
|
||||
*/
|
||||
public void stop(Activity activity) {
|
||||
if (mIsReturning && mExitCallbacks != null) {
|
||||
// Override the previous ActivityOptions. We don't want the
|
||||
// activity to have options since we're essentially canceling the
|
||||
// transition and finishing right now.
|
||||
mActivity.convertToTranslucent(null, null);
|
||||
activity.convertToTranslucent(null, null);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
@@ -434,7 +426,7 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
mSharedElementNotified = true;
|
||||
delayCancel();
|
||||
|
||||
if (!mActivity.isTopOfTask()) {
|
||||
if (mExitCallbacks.isReturnTransitionAllowed()) {
|
||||
mResultReceiver.send(MSG_ALLOW_RETURN_TRANSITION, null);
|
||||
}
|
||||
|
||||
@@ -474,22 +466,20 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
}
|
||||
|
||||
private void finishIfNecessary() {
|
||||
if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty() ||
|
||||
mSharedElementsHidden)) {
|
||||
if (mIsReturning && mExitNotified && mExitCallbacks != null && (mSharedElements.isEmpty()
|
||||
|| mSharedElementsHidden)) {
|
||||
finish();
|
||||
}
|
||||
if (!mIsReturning && mExitNotified) {
|
||||
mActivity = null; // don't need it anymore
|
||||
mExitCallbacks = null; // don't need it anymore
|
||||
}
|
||||
}
|
||||
|
||||
private void finish() {
|
||||
stopCancel();
|
||||
if (mActivity != null) {
|
||||
mActivity.mActivityTransitionState.clear();
|
||||
mActivity.finish();
|
||||
mActivity.overridePendingTransition(0, 0);
|
||||
mActivity = null;
|
||||
if (mExitCallbacks != null) {
|
||||
mExitCallbacks.onFinish();
|
||||
mExitCallbacks = null;
|
||||
}
|
||||
// Clear the state so that we can't hold any references accidentally and leak memory.
|
||||
clearState();
|
||||
@@ -529,7 +519,49 @@ class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
|
||||
}
|
||||
}
|
||||
|
||||
interface HideSharedElementsCallback {
|
||||
void hideSharedElements();
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public interface ExitTransitionCallbacks {
|
||||
|
||||
/**
|
||||
* Returns true if reverse exit animation is supported
|
||||
*/
|
||||
boolean isReturnTransitionAllowed();
|
||||
|
||||
/**
|
||||
* Called then the transition finishes
|
||||
*/
|
||||
void onFinish();
|
||||
|
||||
/**
|
||||
* Optional callback when the transition is hiding elements in the source surface
|
||||
*/
|
||||
default void hideSharedElements() { };
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
public static class ActivityExitTransitionCallbacks implements ExitTransitionCallbacks {
|
||||
|
||||
@NonNull
|
||||
final Activity mActivity;
|
||||
|
||||
ActivityExitTransitionCallbacks(@NonNull Activity activity) {
|
||||
mActivity = activity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReturnTransitionAllowed() {
|
||||
return !mActivity.isTopOfTask();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
mActivity.mActivityTransitionState.clear();
|
||||
mActivity.finish();
|
||||
mActivity.overridePendingTransition(0, 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user