OSDN Git Service

Transfer image of shared element to launched Activity.
authorGeorge Mount <mount@google.com>
Tue, 15 Apr 2014 16:01:32 +0000 (09:01 -0700)
committerGeorge Mount <mount@google.com>
Tue, 29 Apr 2014 20:08:37 +0000 (13:08 -0700)
First pass of the API will use a Bitmap. Future versions will use
more efficient mechanisms.

Change-Id: I111474dd031fef0b86de871017c85dc679166acf

api/current.txt
core/java/android/app/ActivityOptions.java
core/java/android/app/ActivityTransitionCoordinator.java
core/java/android/app/EnterTransitionCoordinator.java
core/res/res/values/attrs.xml
core/res/res/values/ids.xml
core/res/res/values/public.xml

index e65b048..20a9537 100644 (file)
@@ -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();
index 85464c4..a49359f 100644 (file)
@@ -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 {
index d8a356f..f96c3e3 100644 (file)
  */
 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);
-                mSharedElements.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++;
             }
         }
-        if (!mSharedElements.isEmpty()) {
+        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 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;
 
index aa097e0..0798529 100644 (file)
@@ -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();
         }
     }
 }
index e35239f..0a3ca2a 100644 (file)
              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(
index a79e1fe..889c368 100644 (file)
@@ -84,4 +84,5 @@
   <item type="id" name="scene_layoutid_cache" />
   <item type="id" name="shared_element_name" />
   <item type="id" name="mask" />
+  <item type="id" name="shared_element" />
 </resources>
index c7f446b..5ccb05b 100644 (file)
 
   <public type="id" name="shared_element_name" />
   <public type="id" name="mask" />
+  <public type="id" name="shared_element" />
 
   <public-padding type="style" name="l_resource_pad" end="0x01030200" />