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.Matrix;
     22 import android.graphics.RectF;
     23 import android.graphics.drawable.BitmapDrawable;
     24 import android.graphics.drawable.Drawable;
     25 import android.os.Bundle;
     26 import android.os.Parcelable;
     27 import android.transition.TransitionUtils;
     28 import android.view.View;
     29 import android.widget.ImageView;
     30 import android.widget.ImageView.ScaleType;
     31 
     32 import java.util.List;
     33 import java.util.Map;
     34 
     35 /**
     36  * Listener provided in
     37  * {@link Activity#setEnterSharedElementCallback(SharedElementCallback)} and
     38  * {@link Activity#setExitSharedElementCallback(SharedElementCallback)} as well as
     39  * {@link Fragment#setEnterSharedElementCallback(SharedElementCallback)} and
     40  * {@link Fragment#setExitSharedElementCallback(SharedElementCallback)}
     41  * to monitor the Shared element transitions. The events can be used to customize Activity
     42  * and Fragment Transition behavior.
     43  */
     44 public abstract class SharedElementCallback {
     45     private Matrix mTempMatrix;
     46     private static final String BUNDLE_SNAPSHOT_BITMAP = "sharedElement:snapshot:bitmap";
     47     private static final String BUNDLE_SNAPSHOT_IMAGE_SCALETYPE = "sharedElement:snapshot:imageScaleType";
     48     private static final String BUNDLE_SNAPSHOT_IMAGE_MATRIX = "sharedElement:snapshot:imageMatrix";
     49 
     50     static final SharedElementCallback NULL_CALLBACK = new SharedElementCallback() {
     51     };
     52 
     53     /**
     54      * Called immediately after the start state is set for the shared element.
     55      * The shared element will start at the size and position of the shared element
     56      * in the launching Activity or Fragment.
     57      *
     58      * @param sharedElementNames The names of the shared elements that were accepted into
     59      *                           the View hierarchy.
     60      * @param sharedElements The shared elements that are part of the View hierarchy.
     61      * @param sharedElementSnapshots The Views containing snap shots of the shared element
     62      *                               from the launching Window. These elements will not
     63      *                               be part of the scene, but will be positioned relative
     64      *                               to the Window decor View. This list is null for Fragment
     65      *                               Transitions.
     66      */
     67     public void onSharedElementStart(List<String> sharedElementNames,
     68             List<View> sharedElements, List<View> sharedElementSnapshots) {}
     69 
     70     /**
     71      * Called after the end state is set for the shared element, but before the end state
     72      * is captured by the shared element transition.
     73      * <p>
     74      *     Any customization done in
     75      *     {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)}
     76      *     may need to be modified to the final state of the shared element if it is not
     77      *     automatically corrected by layout. For example, rotation or scale will not
     78      *     be affected by layout and if changed in {@link #onSharedElementStart(java.util.List,
     79      *     java.util.List, java.util.List)}, it will also have to be set here again to correct
     80      *     the end state.
     81      * </p>
     82      *
     83      * @param sharedElementNames The names of the shared elements that were accepted into
     84      *                           the View hierarchy.
     85      * @param sharedElements The shared elements that are part of the View hierarchy.
     86      * @param sharedElementSnapshots The Views containing snap shots of the shared element
     87      *                               from the launching Window. These elements will not
     88      *                               be part of the scene, but will be positioned relative
     89      *                               to the Window decor View. This list will be null for
     90      *                               Fragment Transitions.
     91      */
     92     public void onSharedElementEnd(List<String> sharedElementNames,
     93             List<View> sharedElements, List<View> sharedElementSnapshots) {}
     94 
     95     /**
     96      * Called after {@link #onMapSharedElements(java.util.List, java.util.Map)} when
     97      * transferring shared elements in. Any shared elements that have no mapping will be in
     98      * <var>rejectedSharedElements</var>. The elements remaining in
     99      * <var>rejectedSharedElements</var> will be transitioned out of the Scene. If a
    100      * View is removed from <var>rejectedSharedElements</var>, it must be handled by the
    101      * <code>SharedElementCallback</code>.
    102      * <p>
    103      * Views in rejectedSharedElements will have their position and size set to the
    104      * position of the calling shared element, relative to the Window decor View and contain
    105      * snapshots of the View from the calling Activity or Fragment. This
    106      * view may be safely added to the decor View's overlay to remain in position.
    107      * </p>
    108      * <p>This method is not called for Fragment Transitions. All rejected shared elements
    109      * will be handled by the exit transition.</p>
    110      *
    111      * @param rejectedSharedElements Views containing visual information of shared elements
    112      *                               that are not part of the entering scene. These Views
    113      *                               are positioned relative to the Window decor View. A
    114      *                               View removed from this list will not be transitioned
    115      *                               automatically.
    116      */
    117     public void onRejectSharedElements(List<View> rejectedSharedElements) {}
    118 
    119     /**
    120      * Lets the SharedElementCallback adjust the mapping of shared element names to
    121      * Views.
    122      *
    123      * @param names The names of all shared elements transferred from the calling Activity
    124      *              or Fragment in the order they were provided.
    125      * @param sharedElements The mapping of shared element names to Views. The best guess
    126      *                       will be filled into sharedElements based on the transitionNames.
    127      */
    128     public void onMapSharedElements(List<String> names, Map<String, View> sharedElements) {}
    129 
    130     /**
    131      * Creates a snapshot of a shared element to be used by the remote Activity and reconstituted
    132      * with {@link #onCreateSnapshotView(android.content.Context, android.os.Parcelable)}. A
    133      * null return value will mean that the remote Activity will have a null snapshot View in
    134      * {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)} and
    135      * {@link #onSharedElementEnd(java.util.List, java.util.List, java.util.List)}.
    136      *
    137      * <p>This is not called for Fragment Transitions.</p>
    138      *
    139      * @param sharedElement The shared element View to create a snapshot for.
    140      * @param viewToGlobalMatrix A matrix containing a transform from the view to the screen
    141      *                           coordinates.
    142      * @param screenBounds The bounds of shared element in screen coordinate space. This is
    143      *                     the bounds of the view with the viewToGlobalMatrix applied.
    144      * @return A snapshot to send to the remote Activity to be reconstituted with
    145      * {@link #onCreateSnapshotView(android.content.Context, android.os.Parcelable)} and passed
    146      * into {@link #onSharedElementStart(java.util.List, java.util.List, java.util.List)} and
    147      * {@link #onSharedElementEnd(java.util.List, java.util.List, java.util.List)}.
    148      */
    149     public Parcelable onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix,
    150             RectF screenBounds) {
    151         if (sharedElement instanceof ImageView) {
    152             ImageView imageView = ((ImageView) sharedElement);
    153             Drawable d = imageView.getDrawable();
    154             Drawable bg = imageView.getBackground();
    155             if (d != null && (bg == null || bg.getAlpha() == 0)) {
    156                 Bitmap bitmap = TransitionUtils.createDrawableBitmap(d);
    157                 if (bitmap != null) {
    158                     Bundle bundle = new Bundle();
    159                     bundle.putParcelable(BUNDLE_SNAPSHOT_BITMAP, bitmap);
    160                     bundle.putString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE,
    161                             imageView.getScaleType().toString());
    162                     if (imageView.getScaleType() == ScaleType.MATRIX) {
    163                         Matrix matrix = imageView.getImageMatrix();
    164                         float[] values = new float[9];
    165                         matrix.getValues(values);
    166                         bundle.putFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX, values);
    167                     }
    168                     return bundle;
    169                 }
    170             }
    171         }
    172         if (mTempMatrix == null) {
    173             mTempMatrix = new Matrix(viewToGlobalMatrix);
    174         } else {
    175             mTempMatrix.set(viewToGlobalMatrix);
    176         }
    177         return TransitionUtils.createViewBitmap(sharedElement, mTempMatrix, screenBounds);
    178     }
    179 
    180     /**
    181      * Reconstitutes a snapshot View from a Parcelable returned in
    182      * {@link #onCaptureSharedElementSnapshot(android.view.View, android.graphics.Matrix,
    183      * android.graphics.RectF)} to be used in {@link #onSharedElementStart(java.util.List,
    184      * java.util.List, java.util.List)} and {@link #onSharedElementEnd(java.util.List,
    185      * java.util.List, java.util.List)}. The returned View will be sized and positioned after
    186      * this call so that it is ready to be added to the decor View's overlay.
    187      *
    188      * <p>This is not called for Fragment Transitions.</p>
    189      *
    190      * @param context The Context used to create the snapshot View.
    191      * @param snapshot The Parcelable returned by {@link #onCaptureSharedElementSnapshot(
    192      * android.view.View, android.graphics.Matrix, android.graphics.RectF)}.
    193      * @return A View to be sent in {@link #onSharedElementStart(java.util.List, java.util.List,
    194      * java.util.List)} and {@link #onSharedElementEnd(java.util.List, java.util.List,
    195      * java.util.List)}. A null value will produce a null snapshot value for those two methods.
    196      */
    197     public View onCreateSnapshotView(Context context, Parcelable snapshot) {
    198         View view = null;
    199         if (snapshot instanceof Bundle) {
    200             Bundle bundle = (Bundle) snapshot;
    201             Bitmap bitmap = (Bitmap) bundle.getParcelable(BUNDLE_SNAPSHOT_BITMAP);
    202             if (bitmap == null) {
    203                 return null;
    204             }
    205             ImageView imageView = new ImageView(context);
    206             view = imageView;
    207             imageView.setImageBitmap(bitmap);
    208             imageView.setScaleType(
    209                     ScaleType.valueOf(bundle.getString(BUNDLE_SNAPSHOT_IMAGE_SCALETYPE)));
    210             if (imageView.getScaleType() == ScaleType.MATRIX) {
    211                 float[] values = bundle.getFloatArray(BUNDLE_SNAPSHOT_IMAGE_MATRIX);
    212                 Matrix matrix = new Matrix();
    213                 matrix.setValues(values);
    214                 imageView.setImageMatrix(matrix);
    215             }
    216         } else if (snapshot instanceof Bitmap) {
    217             Bitmap bitmap = (Bitmap) snapshot;
    218             view = new View(context);
    219             Resources resources = context.getResources();
    220             view.setBackground(new BitmapDrawable(resources, bitmap));
    221         }
    222         return view;
    223     }
    224 }
    225