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 */ onSharedElementStart(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots)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 */ onSharedElementEnd(List<String> sharedElementNames, List<View> sharedElements, List<View> sharedElementSnapshots)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 */ onRejectSharedElements(List<View> rejectedSharedElements)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 */ onMapSharedElements(List<String> names, Map<String, View> sharedElements)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 */ onCaptureSharedElementSnapshot(View sharedElement, Matrix viewToGlobalMatrix, RectF screenBounds)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 */ onCreateSnapshotView(Context context, Parcelable snapshot)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