Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit f31d367a authored by George Mount's avatar George Mount Committed by Android (Google) Code Review
Browse files

Merge "Transfer image of shared element to launched Activity."

parents 9f595798 caa03107
Loading
Loading
Loading
Loading
+4 −2
Original line number Diff line number Diff line
@@ -1632,6 +1632,7 @@ package android {
    field public static final int selectAll = 16908319; // 0x102001f
    field public static final int selectTextMode = 16908333; // 0x102002d
    field public static final int selectedIcon = 16908302; // 0x102000e
    field public static final int shared_element = 16908355; // 0x1020043
    field public static final int shared_element_name = 16908353; // 0x1020041
    field public static final int startSelectingText = 16908328; // 0x1020028
    field public static final int stopSelectingText = 16908329; // 0x1020029
@@ -3535,8 +3536,9 @@ package android.app {
  public static class ActivityOptions.ActivityTransitionListener {
    ctor public ActivityOptions.ActivityTransitionListener();
    method public android.util.Pair<android.view.View, java.lang.String>[] getSharedElementsMapping();
    method public void onCaptureSharedElementEnd();
    method public void onCaptureSharedElementStart();
    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();
+36 −2
Original line number Diff line number Diff line
@@ -649,13 +649,31 @@ public class ActivityOptions {

        /**
         * 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() {}
        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() {}
        public void onCaptureSharedElementEnd(List<String> sharedElementNames,
                List<View> sharedElements, List<View> sharedElementSnapshots) {}

        /**
         * Called when the enter Transition has been started.
@@ -700,6 +718,22 @@ public class ActivityOptions {
         * 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 {
+115 −19
Original line number Diff line number Diff line
@@ -15,6 +15,12 @@
 */
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.Rect;
import android.os.Bundle;
import android.os.Handler;
@@ -26,8 +32,10 @@ import android.util.ArrayMap;
import android.util.Pair;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroupOverlay;
import android.view.ViewTreeObserver;
import android.view.Window;
import android.widget.ImageView;

import java.util.ArrayList;
import java.util.Collection;
@@ -129,6 +137,7 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
    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";

    /**
     * Sent by the exiting coordinator (either EnterTransitionCoordinator
@@ -305,16 +314,23 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
        setSharedElements();
        reconcileSharedElements(sharedElementNames);
        mEnteringViews.removeAll(mSharedElements);
        setSharedElementState(state);
        final ArrayList<View> accepted = new ArrayList<View>();
        final ArrayList<View> rejected = new ArrayList<View>();
        createSharedElementImages(accepted, rejected, sharedElementNames, state);
        setSharedElementState(state, accepted);
        handleRejected(rejected);

        if (getViewsTransition() != null) {
            setViewVisibility(mEnteringViews, View.INVISIBLE);
        }
        setViewVisibility(mSharedElements, View.VISIBLE);
        Transition transition = beginTransition(mEnteringViews, true, allowOverlappingTransitions(),
                true);

        if (allowOverlappingTransitions()) {
            onStartEnterTransition(transition, mEnteringViews);
        }

        mRemoteResultReceiver.send(MSG_HIDE_SHARED_ELEMENTS, null);
    }

@@ -513,35 +529,59 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
    }

    private void reconcileSharedElements(ArrayList<String> sharedElementNames) {
        Rect epicenter = null;
        for (int i = mTargetSharedNames.size() - 1; i >= 0; i--) {
            if (!sharedElementNames.contains(mTargetSharedNames.get(i))) {
                mTargetSharedNames.remove(i);
        // 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++;
            }
        }
        for (int i = mSharedElements.size() - 1; i >= targetIndex; i--) {
            mSharedElements.remove(i);
            mTargetSharedNames.remove(i);
        }
        }
        if (!mSharedElements.isEmpty()) {
        Rect epicenter = null;
        if (!mTargetSharedNames.isEmpty()
                && mTargetSharedNames.get(0).equals(sharedElementNames.get(0))) {
            epicenter = calcEpicenter(mSharedElements.get(0));
        }
        mEpicenterCallback.setEpicenter(epicenter);
    }

    private void setSharedElementState(Bundle sharedElementState) {
    private void setSharedElementState(Bundle sharedElementState,
            final ArrayList<View> acceptedOverlayViews) {
        final int[] tempLoc = new int[2];
        if (sharedElementState != null) {
            int[] tempLoc = new int[2];
            for (int i = 0; i < mSharedElements.size(); i++) {
                View sharedElement = mSharedElements.get(i);
                View parent = (View) sharedElement.getParent();
                parent.getLocationOnScreen(tempLoc);
                String name = mTargetSharedNames.get(i);
                setSharedElementState(sharedElement, name, sharedElementState, tempLoc);
                sharedElement.requestLayout();
            }
        }
        mListener.onCaptureSharedElementStart();
        mListener.onCaptureSharedElementStart(mTargetSharedNames, mSharedElements,
                acceptedOverlayViews);

        getDecor().getViewTreeObserver().addOnPreDrawListener(
                new ViewTreeObserver.OnPreDrawListener() {
                    @Override
                    public boolean onPreDraw() {
                        getDecor().getViewTreeObserver().removeOnPreDrawListener(this);
                        mListener.onCaptureSharedElementEnd();
                        mListener.onCaptureSharedElementEnd(mTargetSharedNames, mSharedElements,
                                acceptedOverlayViews);
                        return true;
                    }
                }
@@ -555,10 +595,10 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
     * @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 tempLoc A temporary int[2] for capturing the current location of views.
     * @param parentLoc The x and y coordinates of the parent's screen position.
     */
    private static void setSharedElementState(View view, String name, Bundle transitionArgs,
            int[] tempLoc) {
            int[] parentLoc) {
        Bundle sharedElementBundle = transitionArgs.getBundle(name);
        if (sharedElementBundle == null) {
            return;
@@ -576,15 +616,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
        int heightSpec = View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY);
        view.measure(widthSpec, heightSpec);

        ViewGroup parent = (ViewGroup) view.getParent();
        parent.getLocationOnScreen(tempLoc);
        int left = x - tempLoc[0];
        int top = y - tempLoc[1];
        int left = x - parentLoc[0];
        int top = y - parentLoc[1];
        int right = left + width;
        int bottom = top + height;
        view.layout(left, top, right, bottom);

        view.requestLayout();
    }

    /**
@@ -615,6 +651,11 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {

        sharedElementBundle.putString(KEY_NAME, view.getSharedElementName());

        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        view.draw(canvas);
        sharedElementBundle.putParcelable(KEY_BITMAP, bitmap);

        transitionArgs.putBundle(name, sharedElementBundle);
    }

@@ -723,6 +764,61 @@ abstract class ActivityTransitionCoordinator extends ResultReceiver {
        return transition;
    }

    private void handleRejected(final ArrayList<View> rejected) {
        int numRejected = rejected.size();
        if (numRejected == 0) {
            return;
        }
        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.setSharedElementName(name);
                setSharedElementState(imageView, name, state, parentLoc);
                if (mTargetSharedNames.contains(name)) {
                    accepted.add(imageView);
                } else {
                    rejected.add(imageView);
                }
            }
        }
    }

    private static class FixedEpicenterCallback extends Transition.EpicenterCallback {
        private Rect mEpicenter;

+1 −1
Original line number Diff line number Diff line
@@ -264,7 +264,6 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator
        mExitTransitionComplete = true;
        exitAfterSharedElementTransition();
        super.onExitTransitionEnd();
        clearConnections();
    }

    @Override
@@ -287,6 +286,7 @@ class EnterTransitionCoordinator extends ActivityTransitionCoordinator
                mActivity.overridePendingTransition(0, 0);
            }
            notifyExitTransitionComplete();
            clearConnections();
        }
    }
}
+4 −4
Original line number Diff line number Diff line
@@ -462,24 +462,24 @@
             transitions between different window content. -->
        <attr name="windowContentTransitionManager" format="reference" />

        <!-- Reference to a TransitionManager XML resource defining the desired Transition
        <!-- Reference to a Transition XML resource defining the desired Transition
             used to move Views into the initial Window's content Scene. Corresponds to
             {@link android.view.Window#setEnterTransition(android.transition.Transition)}. -->
        <attr name="windowEnterTransition" format="reference"/>

        <!-- Reference to a TransitionManager XML resource defining the desired Transition
        <!-- Reference to a Transition XML resource defining the desired Transition
             used to move Views out of the Window's content Scene when launching a new Activity.
             Corresponds to
             {@link android.view.Window#setExitTransition(android.transition.Transition)}. -->
        <attr name="windowExitTransition" format="reference"/>

        <!-- Reference to a TransitionManager XML resource defining the desired Transition
        <!-- Reference to a Transition XML resource defining the desired Transition
             used to move shared elements transferred into the Window's initial content Scene.
             Corresponds to {@link android.view.Window#setSharedElementEnterTransition(
             android.transition.Transition)}. -->
        <attr name="windowSharedElementEnterTransition" format="reference"/>

        <!-- Reference to a TransitionManager XML resource defining the desired Transition
        <!-- Reference to a Transition XML resource defining the desired Transition
             used when starting a new Activity to move shared elements prior to transferring
             to the called Activity.
             Corresponds to {@link android.view.Window#setSharedElementExitTransition(
Loading