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