SharedElementCallback.java revision 017b13a50c1225ca338357dc1315befa38dc3e33
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 */
16package android.app;
17
18import android.content.Context;
19import android.content.res.Resources;
20import android.graphics.Bitmap;
21import android.graphics.Matrix;
22import android.graphics.RectF;
23import android.graphics.drawable.BitmapDrawable;
24import android.graphics.drawable.Drawable;
25import android.os.Bundle;
26import android.os.Parcelable;
27import android.transition.TransitionUtils;
28import android.view.View;
29import android.widget.ImageView;
30import android.widget.ImageView.ScaleType;
31
32import java.util.List;
33import 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 */
44public 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