Home | History | Annotate | Download | only in app
      1 /*
      2  * Copyright (C) 2014 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package android.app;
     17 
     18 import android.content.Context;
     19 import android.content.res.Resources;
     20 import android.graphics.Bitmap;
     21 import android.graphics.GraphicBuffer;
     22 import android.graphics.Matrix;
     23 import android.graphics.RectF;
     24 import android.graphics.drawable.BitmapDrawable;
     25 import android.graphics.drawable.Drawable;
     26 import android.os.Bundle;
     27 import android.os.Parcelable;
     28 import android.transition.TransitionUtils;
     29 import android.view.View;
     30 import android.view.ViewGroup;
     31 import android.widget.ImageView;
     32 import android.widget.ImageView.ScaleType;
     33 
     34 import java.util.List;
     35 import java.util.Map;
     36 
     37 /**
     38  * Listener provided in
     39  * {@link Activity#setEnterSharedElementCallback(SharedElementCallback)} and
     40  * {@link Activity#setExitSharedElementCallback(SharedElementCallback)} as well as
     41  * {@link Fragment#setEnterSharedElementCallback(SharedElementCallback)} and
     42  * {@link Fragment#setExitSharedElementCallback(SharedElementCallback)}
     43  * to monitor the Shared element transitions. The events can be used to customize Activity
     44  * and Fragment Transition behavior.
     45  */
     46 public abstract class SharedElementCallback {
     47     private Matrix mTempMatrix;
     48     private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap";
     49     private static final String BUNDLE_SNAPSHOT_GRAPHIC_BUFFER =
     50             "sharedElement:snapshot:graphicBuffer";
     51     private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType";
     52     private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix";
     53 
     54     static final SharedElementCallback NULL_CALLBACK = new SharedElementCallback() {
     55     };
     56 
     57     /**
     58      * In Activity Transitions, onSharedElementStart is called immediately before
     59      * capturing the start of the shared element state on enter and reenter transitions and
     60      * immediately before capturing the end of the shared element state for exit and return
     61      * transitions.
     62      * <p>
     63      * In Fragment Transitions, onSharedElementStart is called immediately before capturing the
     64      * start state of all shared element transitions.
     65      * <p>
     66      * This call can be used to adjust the transition start state by modifying the shared
     67      * element Views. Note that no layout step will be executed between onSharedElementStart
     68      * and the transition state capture.
     69      * <p>
     70      * For Activity Transitions, any changes made in {@link #onSharedElementEnd(List, List, List)}
     71      * that are not updated during by layout should be corrected in onSharedElementStart for exit and
     72      * return transitions. For example, rotation or scale will not be affected by layout and
     73      * if changed in {@link #onSharedElementEnd(List, List, List)}, it will also have to be reset
     74      * in onSharedElementStart again to correct the end state.
     75      *
     76      * @param sharedElementNames The names of the shared elements that were accepted into
     77      *                           the View hierarchy.
     78      * @param sharedElements The shared elements that are part of the View hierarchy.
     79      * @param sharedElementSnapshots The Views containing snap shots of the shared element
     80      *                               from the launching Window. These elements will not
     81      *                               be part of the scene, but will be positioned relative
     82      *                               to the Window decor View. This list is null for Fragment
     83      *                               Transitions.
     84      */
     85     public void onSharedElementStart(List<String> sharedElementNames,
     86             List<View> sharedElements, List<View> sharedElementSnapshots) {}
     87 
     88     /**
     89      * In Activity Transitions, onSharedElementEnd is called immediately before
     90      * capturing the end of the shared element state on enter and reenter transitions and
     91      * immediately before capturing the start of the shared element state for exit and return
     92      * transitions.
     93      * <p>
     94      * In Fragment Transitions, onSharedElementEnd is called immediately before capturing the
     95      * end state of all shared element transitions.
     96      * <p>
     97      * This call can be used to adjust the transition end state by modifying the shared
     98      * element Views. Note that no layout step will be executed between onSharedElementEnd
     99      * and the transition state capture.
    100      * <p>
    101      * Any changes made in {@link #onSharedElementStart(List, List, List)} that are not updated
    102      * during layout should be corrected in onSharedElementEnd. For example, rotation or scale
    103      * will not be affected by layout and if changed in
    104      * {@link #onSharedElementStart(List, List, List)}, it will also have to be reset in
    105      * onSharedElementEnd again to correct the end state.
    106      *
    107      * @param sharedElementNames The names of the shared elements that were accepted into
    108      *                           the View hierarchy.
    109      * @param sharedElements The shared elements that are part of the View hierarchy.
    110      * @param sharedElementSnapshots The Views containing snap shots of the shared element
    111      *                               from the launching Window. These elements will not
    112      *                               be part of the scene, but will be positioned relative
    113      *                               to the Window decor View. This list will be null for
    114      *                               Fragment Transitions.
    115      */
    116     public void onSharedElementEnd(List<String> sharedElementNames,
    117             List<View> sharedElements, List<View> sharedElementSnapshots) {}
    118 
    119     /**
    120      * Called after {@link #onMapSharedElements(java.util.List, java.util.Map)} when
    121      * transferring shared elements in. Any shared elements that have no mapping will be in
    122      * <var>rejectedSharedElements</var>. The elements remaining in
    123      * <var>rejectedSharedElements</var> will be transitioned out of the Scene. If a
    124      * View is removed from <var>rejectedSharedElements</var>, it must be handled by the
    125      * <code>SharedElementCallback</code>.
    126      * <p>
    127      * Views in rejectedSharedElements will have their position and size set to the
    128      * position of the calling shared element, relative to the Window decor View and contain
    129      * snapshots of the View from the calling Activity or Fragment. This
    130      * view may be safely added to the decor View's overlay to remain in position.
    131      * </p>
    132      * <p>This method is not called for Fragment Transitions. All rejected shared elements
    133      * will be handled by the exit transition.</p>
    134      *
    135      * @param rejectedSharedElements Views containing visual information of shared elements
    136      *                               that are not part of the entering scene. These Views
    137      *                               are positioned relative to the Window decor View. A
    138      *                               View removed from this list will not be transitioned
    139      *                               automatically.
    140      */
    141     public void onRejectSharedElements(List<View> rejectedSharedElements) {}
    142 
    143     /**
    144      * Lets the SharedElementCallback adjust the mapping of shared element names to
    145      * Views.
    146      *
    147      * @param names The names of all shared elements transferred from the calling Activity
    148      *              or Fragment in the order they were provided.
    149      * @param sharedElements The mapping of shared element names to Views. The best guess
    150      *                       will be filled into sharedElements based on the transitionNames.
    151      */
    152     public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}
    153 
    154     /**
    155      * Creates a snapshot of a shared element to be used by the remote Activity and reconstituted
    156      * with {@link #onCreateSnapshotView(android.content.Context, android.os.Parcelable)}. A
    157      * null return value will mean that the remote Activity will have a null snapshot View in
    158      * {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)} and
    159      * {@link #onSharedElementEnd(java.util.List, java.util.List, java.util.List)}.
    160      *
    161      * <p>This is not called for Fragment Transitions.</p>
    162      *
    163      * @param sharedElement The shared element View to create a snapshot for.
    164      * @param viewToGlobalMatrix A matrix containing a transform from the view to the screen
    165      *                           coordinates.
    166      * @param screenBounds The bounds of shared element in screen coordinate space. This is
    167      *                     the bounds of the view with the viewToGlobalMatrix applied.
    168      * @return A snapshot to send to the remote Activity to be reconstituted with
    169      * {@link #onCreateSnapshotView(android.content.Context, android.os.Parcelable)} and passed
    170      * into {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)} and
    171      * {@link #onSharedElementEnd(java.util.List, java.util.List, java.util.List)}.
    172      */
    173     public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,
    174             RectF screenBounds) {
    175         if (sharedElement instanceof ImageView) {
    176             ImageView imageView = ((ImageView) sharedElement);
    177             Drawable d = imageView.getDrawable();
    178             Drawable bg = imageView.getBackground();
    179             if (d != null && (bg == null || bg.getAlpha() == 0)) {
    180                 Bitmap bitmap = TransitionUtils.createDrawableBitmap(d, imageView);
    181                 if (bitmap != null) {
    182                     Bundle bundle = new Bundle();
    183                     if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
    184                         bundle.putParcelable(BUNDLE_SNAPSHOT_BITMAP, bitmap);
    185                     } else {
    186                         GraphicBuffer graphicBuffer = bitmap.createGraphicBufferHandle();
    187                         bundle.putParcelable(BUNDLE_SNAPSHOT_GRAPHIC_BUFFER, graphicBuffer);
    188                     }
    189                     bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE,
    190                             imageView.getScaleType().toString());
    191                     if (imageView.getScaleType() == ScaleType.MATRIX) {
    192                         Matrix matrix = imageView.getImageMatrix();
    193                         float[] values = new float[9];
    194                         matrix.getValues(values);
    195                         bundle.putFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX, values);
    196                     }
    197                     return bundle;
    198                 }
    199             }
    200         }
    201         if (mTempMatrix == null) {
    202             mTempMatrix = new Matrix(viewToGlobalMatrix);
    203         } else {
    204             mTempMatrix.set(viewToGlobalMatrix);
    205         }
    206         ViewGroup parent = (ViewGroup) sharedElement.getParent();
    207         return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds, parent);
    208     }
    209 
    210     /**
    211      * Reconstitutes a snapshot View from a Parcelable returned in
    212      * {@link #onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix,
    213      * android.graphics.RectF)} to be used in {@link #onSharedElementStart(java.util.List,
    214      * java.util.List, java.util.List)} and {@link #onSharedElementEnd(java.util.List,
    215      * java.util.List, java.util.List)}. The returned View will be sized and positioned after
    216      * this call so that it is ready to be added to the decor View's overlay.
    217      *
    218      * <p>This is not called for Fragment Transitions.</p>
    219      *
    220      * @param context The Context used to create the snapshot View.
    221      * @param snapshot The Parcelable returned by {@link #onCaptureSharedElementSnapshot(
    222      * android.view.View, android.graphics.Matrix, android.graphics.RectF)}.
    223      * @return A View to be sent in {@link #onSharedElementStart(java.util.List, java.util.List,
    224      * java.util.List)} and {@link #onSharedElementEnd(java.util.List, java.util.List,
    225      * java.util.List)}. A null value will produce a null snapshot value for those two methods.
    226      */
    227     public View onCreateSnapshotView(Context context, Parcelable snapshot) {
    228         View view = null;
    229         if (snapshot instanceof Bundle) {
    230             Bundle bundle = (Bundle) snapshot;
    231             GraphicBuffer buffer = bundle.getParcelable(BUNDLE_SNAPSHOT_GRAPHIC_BUFFER);
    232             Bitmap bitmap = bundle.getParcelable(BUNDLE_SNAPSHOT_BITMAP);
    233             if (buffer == null && bitmap == null) {
    234                 return null;
    235             }
    236             if (bitmap == null) {
    237                 bitmap = Bitmap.createHardwareBitmap(buffer);
    238             }
    239             ImageView imageView = new ImageView(context);
    240             view = imageView;
    241             imageView.setImageBitmap(bitmap);
    242             imageView.setScaleType(
    243                     ScaleType.valueOf(bundle.getString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE)));
    244             if (imageView.getScaleType() == ScaleType.MATRIX) {
    245                 float[] values = bundle.getFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX);
    246                 Matrix matrix = new Matrix();
    247                 matrix.setValues(values);
    248                 imageView.setImageMatrix(matrix);
    249             }
    250         } else if (snapshot instanceof Bitmap) {
    251             Bitmap bitmap = (Bitmap) snapshot;
    252             view = new View(context);
    253             Resources resources = context.getResources();
    254             view.setBackground(new BitmapDrawable(resources, bitmap));
    255         }
    256         return view;
    257     }
    258 
    259     /**
    260      * Called during an Activity Transition when the shared elements have arrived at the
    261      * final location and are ready to be transferred. This method is called for both the
    262      * source and destination Activities.
    263      * <p>
    264      * When the shared elements are ready to be transferred,
    265      * {@link OnSharedElementsReadyListener#onSharedElementsReady()}
    266      * must be called to trigger the transfer.
    267      * <p>
    268      * The default behavior is to trigger the transfer immediately.
    269      *
    270      * @param sharedElementNames The names of the shared elements that are being transferred..
    271      * @param sharedElements The shared elements that are part of the View hierarchy.
    272      * @param listener The listener to call when the shared elements are ready to be hidden
    273      *                 in the source Activity or shown in the destination Activity.
    274      */
    275     public void onSharedElementsArrived(List<String> sharedElementNames,
    276             List<View> sharedElements, OnSharedElementsReadyListener listener) {
    277         listener.onSharedElementsReady();
    278     }
    279 
    280     /**
    281      * Listener to be called after {@link
    282      * SharedElementCallback#onSharedElementsArrived(List, List, OnSharedElementsReadyListener)}
    283      * when the shared elements are ready to be hidden in the source Activity and shown in the
    284      * destination Activity.
    285      */
    286     public interface OnSharedElementsReadyListener {
    287 
    288         /**
    289          * Call this method during or after the OnSharedElementsReadyListener has been received
    290          * in {@link SharedElementCallback#onSharedElementsArrived(List, List,
    291          * OnSharedElementsReadyListener)} to indicate that the shared elements are ready to be
    292          * hidden in the source and shown in the destination Activity.
    293          */
    294         void onSharedElementsReady();
    295     }
    296 }
    297