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