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