blob: ba1638ffd1d023dfce42fed05630cdf590d5afa1 [file] [log] [blame]
/*
* 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.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Intent;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.transition.Transition;
import android.transition.TransitionManager;
import android.view.View;
import android.view.ViewTreeObserver;
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.
*/
class ExitTransitionCoordinator extends ActivityTransitionCoordinator {
private static final String TAG = "ExitTransitionCoordinator";
private static final long MAX_WAIT_MS = 1000;
private boolean mExitComplete;
private Bundle mSharedElementBundle;
private boolean mExitNotified;
private boolean mSharedElementNotified;
private Activity mActivity;
private boolean mIsBackgroundReady;
private boolean mIsCanceled;
private Handler mHandler;
private ObjectAnimator mBackgroundAnimator;
private boolean mIsHidden;
private boolean mExitTransitionStarted;
private Bundle mExitSharedElementBundle;
public ExitTransitionCoordinator(Activity activity, ArrayList<String> names,
ArrayList<String> accepted, ArrayList<String> mapped, boolean isReturning) {
super(activity.getWindow(), names, accepted, mapped, getListener(activity, isReturning),
isReturning);
mIsBackgroundReady = !isReturning;
mActivity = activity;
}
private static SharedElementListener getListener(Activity activity, boolean isReturning) {
return isReturning ? activity.mEnterTransitionListener : activity.mExitTransitionListener;
}
@Override
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);
mIsHidden = true;
break;
case MSG_SHARED_ELEMENT_DESTINATION:
mExitSharedElementBundle = resultData;
if (mExitTransitionStarted) {
startSharedElementExit();
}
break;
}
}
private void startSharedElementExit() {
if (!mSharedElements.isEmpty() && getSharedElementTransition() != null) {
Transition transition = getSharedElementExitTransition();
TransitionManager.beginDelayedTransition(getDecor(), transition);
ArrayList<View> sharedElementSnapshots = createSnapshots(mExitSharedElementBundle,
mSharedElementNames);
setSharedElementState(mExitSharedElementBundle, sharedElementSnapshots);
}
}
private void hideSharedElements() {
setViewVisibility(mSharedElements, View.INVISIBLE);
finishIfNecessary();
}
public void startExit() {
beginTransitions();
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);
Transition sharedElementTransition = mSharedElements.isEmpty()
? null : getSharedElementTransition();
if (sharedElementTransition == null) {
sharedElementTransitionComplete();
}
Transition transition = mergeTransitions(sharedElementTransition, getExitTransition());
if (transition == null) {
mExitTransitionStarted = true;
} else {
TransitionManager.beginDelayedTransition(getDecor(), transition);
setViewVisibility(mTransitioningViews, View.INVISIBLE);
getDecor().getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
@Override
public boolean onPreDraw() {
getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
mExitTransitionStarted = true;
if (mExitSharedElementBundle != null) {
startSharedElementExit();
}
notifyComplete();
return true;
}
});
}
}
private void fadeOutBackground() {
if (mBackgroundAnimator == null) {
Drawable background = getDecor().getBackground();
mBackgroundAnimator = ObjectAnimator.ofInt(background, "alpha", 0);
mBackgroundAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mBackgroundAnimator = null;
if (!mIsCanceled) {
mIsBackgroundReady = true;
notifyComplete();
}
}
});
mBackgroundAnimator.setDuration(FADE_BACKGROUND_DURATION_MS);
mBackgroundAnimator.start();
}
}
private Transition getExitTransition() {
Transition viewsTransition = null;
if (!mTransitioningViews.isEmpty()) {
viewsTransition = configureTransition(getViewsTransition());
}
if (viewsTransition == null) {
exitTransitionComplete();
} else {
viewsTransition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
exitTransitionComplete();
if (mIsHidden) {
setViewVisibility(mTransitioningViews, View.VISIBLE);
}
}
@Override
public void onTransitionCancel(Transition transition) {
super.onTransitionCancel(transition);
}
});
}
return viewsTransition;
}
private Transition getSharedElementExitTransition() {
Transition sharedElementTransition = null;
if (!mSharedElements.isEmpty()) {
sharedElementTransition = configureTransition(getSharedElementTransition());
}
if (sharedElementTransition == null) {
sharedElementTransitionComplete();
} else {
sharedElementTransition.addListener(new Transition.TransitionListenerAdapter() {
@Override
public void onTransitionEnd(Transition transition) {
sharedElementTransitionComplete();
if (mIsHidden) {
setViewVisibility(mSharedElements, View.VISIBLE);
}
}
});
mSharedElements.get(0).invalidate();
}
return sharedElementTransition;
}
private void beginTransitions() {
Transition sharedElementTransition = getSharedElementExitTransition();
Transition viewsTransition = getExitTransition();
Transition transition = mergeTransitions(sharedElementTransition, viewsTransition);
mExitTransitionStarted = true;
if (transition != null) {
TransitionManager.beginDelayedTransition(getDecor(), transition);
}
}
private void exitTransitionComplete() {
mExitComplete = true;
notifyComplete();
}
protected boolean isReadyToNotify() {
return mSharedElementBundle != null && mResultReceiver != null && mIsBackgroundReady
&& mExitTransitionStarted;
}
private void sharedElementTransitionComplete() {
mSharedElementBundle = captureSharedElementState();
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
finishIfNecessary();
}
}
}
private void finishIfNecessary() {
if (mIsReturning && mExitNotified && mActivity != null && (mSharedElements.isEmpty()
|| mSharedElements.get(0).getVisibility() == View.INVISIBLE)) {
mActivity.finish();
mActivity.overridePendingTransition(0, 0);
mActivity = null;
}
if (!mIsReturning && mExitNotified) {
mActivity = null; // don't need it anymore
}
}
@Override
protected Transition getViewsTransition() {
if (mIsReturning) {
return getWindow().getEnterTransition();
} else {
return getWindow().getExitTransition();
}
}
protected Transition getSharedElementTransition() {
if (mIsReturning) {
return getWindow().getSharedElementEnterTransition();
} else {
return getWindow().getSharedElementExitTransition();
}
}
}