diff options
author | George Mount <mount@google.com> | 2014-04-07 16:22:46 +0000 |
---|---|---|
committer | Android Git Automerger <android-git-automerger@android.com> | 2014-04-07 16:22:46 +0000 |
commit | f4360efb52734774db3ab0b2df50728840a6336d (patch) | |
tree | 3bafacb1ac9dbb42cdfc6c42e98f273d8ac3f7a4 /policy | |
parent | 45ac9d45c48431d316282e0953eeeffdc0457139 (diff) | |
parent | dde10d073f819e78c0b26d7f37ed10f01b45125c (diff) | |
download | frameworks_base-f4360efb52734774db3ab0b2df50728840a6336d.zip frameworks_base-f4360efb52734774db3ab0b2df50728840a6336d.tar.gz frameworks_base-f4360efb52734774db3ab0b2df50728840a6336d.tar.bz2 |
am dde10d07: Merge "Implement "Back" for Activity Transitions."
* commit 'dde10d073f819e78c0b26d7f37ed10f01b45125c':
Implement "Back" for Activity Transitions.
Diffstat (limited to 'policy')
-rw-r--r-- | policy/src/com/android/internal/policy/impl/PhoneWindow.java | 420 |
1 files changed, 324 insertions, 96 deletions
diff --git a/policy/src/com/android/internal/policy/impl/PhoneWindow.java b/policy/src/com/android/internal/policy/impl/PhoneWindow.java index f1db904..f87d0ba 100644 --- a/policy/src/com/android/internal/policy/impl/PhoneWindow.java +++ b/policy/src/com/android/internal/policy/impl/PhoneWindow.java @@ -25,6 +25,8 @@ import static android.view.WindowManager.LayoutParams.*; import android.animation.Animator; import android.animation.ObjectAnimator; import android.app.ActivityOptions; +import android.os.Looper; +import android.transition.Fade; import android.transition.Scene; import android.transition.Transition; import android.transition.TransitionInflater; @@ -109,7 +111,6 @@ import android.widget.TextView; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; -import java.util.List; import java.util.Map; /** @@ -253,8 +254,10 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private ActivityOptions mActivityOptions; private SceneTransitionListener mSceneTransitionListener; - private boolean mTriggerEarly = true; + private boolean mAllowEnterOverlap = true; + private boolean mAllowExitOverlap = true; private Map<String, String> mSharedElementsMap; + private ArrayList<View> mTransitioningViews; static class WindowManagerHolder { static final IWindowManager sWindowManager = IWindowManager.Stub.asInterface( @@ -4081,14 +4084,26 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void setTransitionOptions(ActivityOptions options, SceneTransitionListener listener) { + public void setTransitionOptions(Bundle options, SceneTransitionListener listener) { mSceneTransitionListener = listener; - mActivityOptions = options; + ActivityOptions activityOptions = null; + if (options != null) { + activityOptions = new ActivityOptions(options); + if (activityOptions.getAnimationType() != ActivityOptions.ANIM_SCENE_TRANSITION) { + activityOptions = null; + } + } + mActivityOptions = activityOptions; + } + + @Override + public void setAllowOverlappingEnterTransition(boolean allow) { + mAllowEnterOverlap = allow; } @Override - public void setTriggerEarlyEnterTransition(boolean triggerEarly) { - mTriggerEarly = triggerEarly; + public void setAllowOverlappingExitTransition(boolean allow) { + mAllowExitOverlap = allow; } @Override @@ -4097,7 +4112,48 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public Bundle startExitTransition(ActivityOptions activityOptions) { + public void restoreViewVisibilityAfterTransitionToCallee() { + if (mTransitioningViews != null) { + setViewVisibility(mTransitioningViews, View.VISIBLE); + } + } + + @Override + public void startExitTransitionToCaller(final Runnable onTransitionEnd) { + Transition transition; + if (mContentScene == null || mTransitionManager == null || mActivityOptions == null + || (transition = mTransitionManager.getEnterTransition(mContentScene)) == null) { + onTransitionEnd.run(); + return; + } + if (mAllowExitOverlap) { + TransitionSet transitionSet = new TransitionSet(); + transitionSet.addTransition(transition); + Fade fade = new Fade(); + transitionSet.addTransition(fade); + transition = transitionSet; + } + + final ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); + mapSharedElements(sharedElements); + final Bundle sharedElementArgs = new Bundle(); + captureTerminalSharedElementState(sharedElements, sharedElementArgs); + + final ArrayList<View> transitioningViews = new ArrayList<View>(); + mDecor.captureTransitioningViews(transitioningViews); + transitioningViews.removeAll(sharedElements.values()); + + mSceneTransitionListener.convertToTranslucent(); + + ExitSceneBack exitScene = + new ExitSceneBack(onTransitionEnd, sharedElementArgs, sharedElements.values()); + exitScene.start(transition); + mTransitionManager.beginDelayedTransition(mDecor, transition); + setViewVisibility(transitioningViews, View.INVISIBLE); + } + + @Override + public Bundle startExitTransitionToCallee(Bundle options) { if (mContentScene == null) { return null; } @@ -4106,16 +4162,15 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return null; } + ActivityOptions activityOptions = new ActivityOptions(options); ArrayMap<String, View> sharedElements = findSharedElements(activityOptions); // Find exiting Views and shared elements - final ArrayList<View> transitioningViews = new ArrayList<View>(); - mDecor.captureTransitioningViews(transitioningViews); - transitioningViews.removeAll(sharedElements.values()); + ArrayList<View> transitioningViews = captureTransitioningViews(sharedElements.values()); - Transition exitTransition = cloneAndSetTransitionTargets(transition, + Transition exitTransition = addTransitionTargets(transition, transitioningViews, true); - Transition sharedElementTransition = cloneAndSetTransitionTargets(transition, + Transition sharedElementTransition = addTransitionTargets(transition, transitioningViews, false); // transitionSet is the total exit transition, including hero animation. @@ -4124,7 +4179,7 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { transitionSet.addTransition(sharedElementTransition); updateExitActivityOptions(activityOptions, sharedElements, - sharedElementTransition, exitTransition); + sharedElementTransition, transitioningViews, exitTransition); // Start exiting the Views that need to exit TransitionManager.beginDelayedTransition(mDecor, transitionSet); @@ -4133,6 +4188,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return activityOptions.toBundle(); } + private ArrayList<View> captureTransitioningViews(Collection<View> sharedElements) { + mTransitioningViews = new ArrayList<View>(); + mDecor.captureTransitioningViews(mTransitioningViews); + ArrayList<View> transitioningViews = (ArrayList<View>) mTransitioningViews.clone(); + transitioningViews.removeAll(sharedElements); + return transitioningViews; + } + private ArrayMap<String, View> findSharedElements(ActivityOptions activityOptions) { ArrayMap<String, View> sharedElements = new ArrayMap<String, View>(); mDecor.findSharedElements(sharedElements); @@ -4149,9 +4212,17 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return sharedElements; } + private static void runOnUiThread(Handler handler, Runnable runnable) { + if (handler.getLooper() != Looper.myLooper()) { + handler.post(runnable); + } else { + runnable.run(); + } + } + private void updateExitActivityOptions(ActivityOptions activityOptions, final Map<String, View> sharedElements, Transition sharedElementTransition, - Transition exitTransition) { + final ArrayList<View> transitioningViews, Transition exitTransition) { // Schedule capturing of the shared element state final Bundle sharedElementArgs = new Bundle(); @@ -4159,30 +4230,84 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { ActivityOptions.SharedElementSource sharedElementSource = new ActivityOptions.SharedElementSource() { + private Handler mHandler = new Handler(); + @Override public Bundle getSharedElementExitState() { return sharedElementArgs; } @Override - public void acceptedSharedElements(ArrayList<String> sharedElementNames) { + public void acceptedSharedElements(final ArrayList<String> sharedElementNames) { if (sharedElementNames.size() == sharedElements.size()) { return; // They were all accepted } - Transition transition = mTransitionManager.getExitTransition(mContentScene).clone(); - TransitionManager.beginDelayedTransition(mDecor, transition); - for (String name: sharedElements.keySet()) { - if (!sharedElementNames.contains(name)) { - sharedElements.get(name).setVisibility(View.INVISIBLE); + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + Transition transition = mTransitionManager.getExitTransition(mContentScene); + TransitionManager.beginDelayedTransition(mDecor, transition); + for (String name : sharedElements.keySet()) { + if (!sharedElementNames.contains(name)) { + sharedElements.get(name).setVisibility(View.INVISIBLE); + } + } + sharedElements.keySet().retainAll(sharedElementNames); } - } - sharedElements.keySet().retainAll(sharedElementNames); + }); } @Override public void hideSharedElements() { if (sharedElements != null) { - setViewVisibility(sharedElements.values(), View.INVISIBLE); + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + setViewVisibility(sharedElements.values(), View.INVISIBLE); + } + }); + } + } + + @Override + public void restore(final Bundle sharedElementState) { + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + mTransitioningViews = null; + Transition transition = mTransitionManager.getExitTransition(mContentScene); + setSharedElementState(sharedElements, sharedElementState); + setViewVisibility(sharedElements.values(), View.VISIBLE); + if (mSceneTransitionListener != null) { + mSceneTransitionListener.sharedElementStart(transition); + mDecor.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mDecor.getViewTreeObserver().removeOnPreDrawListener(this); + mSceneTransitionListener.sharedElementEnd(); + return true; + } + }); + } + TransitionManager.beginDelayedTransition(mDecor, transition); + setViewVisibility(transitioningViews, View.VISIBLE); + for (View sharedElement: sharedElements.values()) { + sharedElement.requestLayout(); + } + } + }); + } + + @Override + public void prepareForRestore() { + if (mTransitioningViews != null) { + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + setViewVisibility(mTransitioningViews, View.INVISIBLE); + } + }); } } }; @@ -4207,22 +4332,18 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { }); } - private static Transition cloneAndSetTransitionTargets(Transition transition, - List<View> views, boolean add) { - transition = transition.clone(); - if (!transition.getTargetIds().isEmpty() || !transition.getTargets().isEmpty()) { - TransitionSet set = new TransitionSet(); - set.addTransition(transition); - transition = set; - } + private static Transition addTransitionTargets(Transition transition, Collection<View> views, + boolean add) { + TransitionSet set = new TransitionSet(); + set.addTransition(transition); for (View view: views) { if (add) { - transition.addTarget(view); + set.addTarget(view); } else { - transition.excludeTarget(view, true); + set.excludeTarget(view, true); } } - return transition; + return set; } private static void setViewVisibility(Collection<View> views, int visibility) { @@ -4231,6 +4352,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } } + private static void setSharedElementState(Map<String, View> sharedElements, + Bundle sharedElementState) { + int[] tempLoc = new int[2]; + for (Map.Entry<String, View> entry: sharedElements.entrySet()) { + setSharedElementState(entry.getValue(), entry.getKey(), sharedElementState, tempLoc); + } + } + /** * Sets the captured values from a previous * {@link #captureSharedElementState(android.view.View, String, android.os.Bundle, int[])} @@ -4247,23 +4376,27 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { return; } - int x = sharedElementBundle.getInt(KEY_SCREEN_X); - view.getLocationOnScreen(tempLoc); - int offsetX = x - tempLoc[0]; - view.offsetLeftAndRight(offsetX); + 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); - view.setRight(view.getLeft() + width); + int height = sharedElementBundle.getInt(KEY_HEIGHT); - int y = sharedElementBundle.getInt(KEY_SCREEN_Y); - int offsetY = y - tempLoc[1]; - view.offsetTopAndBottom(offsetY); + int widthSpec = View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY); + int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY); + view.measure(widthSpec, heightSpec); - int height = sharedElementBundle.getInt(KEY_HEIGHT); - view.setBottom(view.getTop() + height); + ViewGroup parent = (ViewGroup) view.getParent(); + parent.getLocationOnScreen(tempLoc); + int left = x - tempLoc[0]; + int top = y - tempLoc[1]; + int right = left + width; + int bottom = top + height; + view.layout(left, top, right, bottom); - float z = sharedElementBundle.getFloat(KEY_TRANSLATION_Z); - view.setTranslationZ(z); + view.requestLayout(); } /** @@ -4297,6 +4430,102 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { transitionArgs.putBundle(name, sharedElementBundle); } + private void mapSharedElements(ArrayMap<String, View> sharedElements) { + ArrayList<String> sharedElementNames = mActivityOptions.getSharedElementNames(); + if (sharedElementNames != null) { + mDecor.findSharedElements(sharedElements); + if (mSharedElementsMap != null) { + for (Map.Entry<String, String> entry : mSharedElementsMap.entrySet()) { + View sharedElement = sharedElements.remove(entry.getValue()); + if (sharedElement != null) { + sharedElements.put(entry.getKey(), sharedElement); + } + } + } + sharedElements.keySet().retainAll(sharedElementNames); + } + } + + private class ExitSceneBack extends Transition.TransitionListenerAdapter implements + Animator.AnimatorListener { + private boolean mExitTransitionComplete; + private boolean mBackgroundFadeComplete; + private boolean mOnCompleteExecuted; + private boolean mSharedElementTransitioned; + private Runnable mOnComplete; + private Bundle mSharedElementArgs; + private Collection<View> mSharedElements; + + public ExitSceneBack(Runnable onComplete, Bundle sharedElementArgs, + Collection<View> sharedElements) { + mOnComplete = onComplete; + mSharedElementArgs = sharedElementArgs; + mSharedElements = sharedElements; + } + + public void start(Transition exitTransition) { + if (mActivityOptions != null) { + mActivityOptions.dispatchPrepareRestore(); + } + exitTransition.addListener(this); + Drawable background = mDecor.getBackground(); + if (background != null) { + ObjectAnimator animator = ObjectAnimator.ofInt(background, "alpha", 0); + animator.addListener(this); + animator.start(); + } else { + mBackgroundFadeComplete = true; + startCalledActivityEnter(); + } + } + + @Override + public void onTransitionEnd(Transition transition) { + transition.removeListener(this); + mExitTransitionComplete = true; + notifyComplete(); + if (!mAllowExitOverlap) { + startCalledActivityEnter(); + } + } + + private void notifyComplete() { + if (mExitTransitionComplete && mBackgroundFadeComplete + && mSharedElementTransitioned && !mOnCompleteExecuted) { + mOnComplete.run(); + mSceneTransitionListener.nullPendingTransition(); + mOnCompleteExecuted = true; + } + } + + @Override + public void onAnimationStart(Animator animation) { + } + + @Override + public void onAnimationEnd(Animator animation) { + mBackgroundFadeComplete = true; + if (mAllowExitOverlap) { + startCalledActivityEnter(); + } + } + + @Override + public void onAnimationCancel(Animator animation) { + } + + @Override + public void onAnimationRepeat(Animator animation) { + } + + private void startCalledActivityEnter() { + mActivityOptions.dispatchRestore(mSharedElementArgs); + setViewVisibility(mSharedElements, View.INVISIBLE); + mSharedElementTransitioned = true; + notifyComplete(); + } + } + /** * Provides code for handling the Activity transition entering scene. * When the first scene is laid out (onPreDraw), it makes views invisible. @@ -4335,33 +4564,22 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { if (!mEnterTransitionStarted && mSceneTransitionListener != null) { mEnterTransitionStarted = true; mDecor.captureTransitioningViews(mEnteringViews); - ArrayList<String> sharedElementNames = mActivityOptions.getSharedElementNames(); - if (sharedElementNames != null) { - mDecor.findSharedElements(mSharedElementTargets); - if (mSharedElementsMap != null) { - for (Map.Entry<String, String> entry : mSharedElementsMap.entrySet()) { - View sharedElement = mSharedElementTargets.remove(entry.getValue()); - if (sharedElement != null) { - mSharedElementTargets.put(entry.getKey(), sharedElement); - } - } - } - mSharedElementTargets.keySet().retainAll(sharedElementNames); - mEnteringViews.removeAll(mSharedElementTargets.values()); - } + mapSharedElements(mSharedElementTargets); + mEnteringViews.removeAll(mSharedElementTargets.values()); setViewVisibility(mEnteringViews, View.INVISIBLE); setViewVisibility(mSharedElementTargets.values(), View.INVISIBLE); - if (mTriggerEarly) { + if (mAllowEnterOverlap) { beginEnterScene(); } observer.addOnPreDrawListener(this); + return false; } else { mHandler.postDelayed(this, MAX_TRANSITION_START_WAIT); mActivityOptions.dispatchSceneTransitionStarted(this, new ArrayList<String>(mSharedElementTargets.keySet())); + return !mSharedElementReadyReceived; } - return true; } public void start() { @@ -4375,43 +4593,47 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { } @Override - public void sharedElementTransitionComplete(Bundle transitionArgs) { + public void sharedElementTransitionComplete(final Bundle transitionArgs) { if (!mSharedElementReadyReceived) { mSharedElementReadyReceived = true; mHandler.removeCallbacks(this); mHandler.postDelayed(this, MAX_TRANSITION_FINISH_WAIT); if (!mSharedElementTargets.isEmpty()) { - Transition transition = getTransitionManager().getEnterTransition( - mContentScene); - if (transition == null) { - transition = TransitionManager.getDefaultTransition(); - } - transition = transition.clone(); - if (transitionArgs == null) { - TransitionManager.beginDelayedTransition(mDecor, transition); - setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); - } else { - int[] tempLoc = new int[2]; - for (Map.Entry<String, View> entry: mSharedElementTargets.entrySet()) { - setSharedElementState(entry.getValue(), entry.getKey(), transitionArgs, - tempLoc); + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + Transition transition = getTransitionManager().getEnterTransition( + mContentScene); + if (transition == null) { + transition = TransitionManager.getDefaultTransition(); + } + transition = addTransitionTargets(transition, + mSharedElementTargets.values(), + true); + if (transitionArgs == null) { + TransitionManager.beginDelayedTransition(mDecor, transition); + setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); + } else { + mSceneTransitionListener.sharedElementStart(transition); + setSharedElementState(mSharedElementTargets, transitionArgs); + setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); + mDecor.getViewTreeObserver().addOnPreDrawListener( + new ViewTreeObserver.OnPreDrawListener() { + @Override + public boolean onPreDraw() { + mDecor.getViewTreeObserver() + .removeOnPreDrawListener(this); + mSceneTransitionListener.sharedElementEnd(); + mActivityOptions.dispatchSharedElementsReady(); + return true; + } + }); + TransitionManager.beginDelayedTransition(mDecor, transition); + } } - setViewVisibility(mSharedElementTargets.values(), View.VISIBLE); - mSceneTransitionListener.sharedElementStart(transition); - mDecor.getViewTreeObserver().addOnPreDrawListener( - new ViewTreeObserver.OnPreDrawListener() { - @Override - public boolean onPreDraw() { - mDecor.getViewTreeObserver().removeOnPreDrawListener(this); - mSceneTransitionListener.sharedElementEnd(); - mActivityOptions.dispatchSharedElementsReady(); - return true; - } - }); - TransitionManager.beginDelayedTransition(mDecor, transition); - } + }); } - if (mTriggerEarly) { + if (mAllowEnterOverlap) { fadeInBackground(); } } @@ -4436,9 +4658,14 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { mAllDone = true; sharedElementTransitionComplete(null); mHandler.removeCallbacks(this); - if (!mTriggerEarly) { - beginEnterScene(); - fadeInBackground(); + if (!mAllowEnterOverlap) { + runOnUiThread(mHandler, new Runnable() { + @Override + public void run() { + beginEnterScene(); + fadeInBackground(); + } + }); } } @@ -4462,8 +4689,9 @@ public class PhoneWindow extends Window implements MenuBuilder.Callback { private void beginEnterScene() { Transition transition = getTransitionManager().getEnterTransition(mContentScene); if (transition == null) { - transition = TransitionManager.getDefaultTransition().clone(); + transition = TransitionManager.getDefaultTransition(); } + transition = addTransitionTargets(transition, mEnteringViews, true); TransitionManager.beginDelayedTransition(mDecor, transition); setViewVisibility(mEnteringViews, View.VISIBLE); } |