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