Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2013 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 | */ |
Chet Haase | 6ebe3de | 2013-06-17 16:50:50 -0700 | [diff] [blame] | 16 | |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 17 | package android.view.transition; |
| 18 | |
Chet Haase | e9d32ea | 2013-06-04 08:46:42 -0700 | [diff] [blame] | 19 | import android.util.ArrayMap; |
Chet Haase | c43524f | 2013-07-16 14:40:11 -0700 | [diff] [blame] | 20 | import android.util.Log; |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 21 | import android.view.ViewGroup; |
| 22 | import android.view.ViewTreeObserver; |
| 23 | |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 24 | import java.util.ArrayList; |
| 25 | |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 26 | /** |
| 27 | * This class manages the set of transitions that fire when there is a |
| 28 | * change of {@link Scene}. To use the manager, add scenes along with |
| 29 | * transition objects with calls to {@link #setTransition(Scene, Transition)} |
| 30 | * or {@link #setTransition(Scene, Scene, Transition)}. Setting specific |
| 31 | * transitions for scene changes is not required; by default, a Scene change |
| 32 | * will use {@link AutoTransition} to do something reasonable for most |
| 33 | * situations. Specifying other transitions for particular scene changes is |
| 34 | * only necessary if the application wants different transition behavior |
| 35 | * in these situations. |
| 36 | */ |
| 37 | public class TransitionManager { |
| 38 | // TODO: how to handle enter/exit? |
| 39 | |
Chet Haase | c43524f | 2013-07-16 14:40:11 -0700 | [diff] [blame] | 40 | private static String LOG_TAG = "TransitionManager"; |
| 41 | |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 42 | private static final Transition sDefaultTransition = new AutoTransition(); |
| 43 | private Transition mDefaultTransition = new AutoTransition(); |
| 44 | |
Chet Haase | 0873518 | 2013-06-04 10:44:40 -0700 | [diff] [blame] | 45 | ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>(); |
| 46 | ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions = |
| 47 | new ArrayMap<Scene, ArrayMap<Scene, Transition>>(); |
Chet Haase | e9d32ea | 2013-06-04 08:46:42 -0700 | [diff] [blame] | 48 | static ArrayMap<ViewGroup, Transition> sRunningTransitions = |
| 49 | new ArrayMap<ViewGroup, Transition>(); |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 50 | private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>(); |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 51 | |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 52 | |
| 53 | /** |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 54 | * Sets the transition to be used for any scene change for which no |
| 55 | * other transition is explicitly set. The initial value is |
| 56 | * an {@link AutoTransition} instance. |
| 57 | * |
| 58 | * @param transition The default transition to be used for scene changes. |
| 59 | */ |
| 60 | public void setDefaultTransition(Transition transition) { |
| 61 | mDefaultTransition = transition; |
| 62 | } |
| 63 | |
| 64 | /** |
| 65 | * Gets the current default transition. The initial value is an {@link |
| 66 | * AutoTransition} instance. |
| 67 | * |
| 68 | * @return The current default transition. |
| 69 | * @see #setDefaultTransition(Transition) |
| 70 | */ |
| 71 | public Transition getDefaultTransition() { |
| 72 | return mDefaultTransition; |
| 73 | } |
| 74 | |
| 75 | /** |
| 76 | * Sets a specific transition to occur when the given scene is entered. |
| 77 | * |
| 78 | * @param scene The scene which, when applied, will cause the given |
| 79 | * transition to run. |
| 80 | * @param transition The transition that will play when the given scene is |
| 81 | * entered. A value of null will result in the default behavior of |
| 82 | * using {@link AutoTransition}. |
| 83 | */ |
| 84 | public void setTransition(Scene scene, Transition transition) { |
| 85 | mSceneTransitions.put(scene, transition); |
| 86 | } |
| 87 | |
| 88 | /** |
| 89 | * Sets a specific transition to occur when the given pair of scenes is |
| 90 | * exited/entered. |
| 91 | * |
| 92 | * @param fromScene The scene being exited when the given transition will |
| 93 | * be run |
| 94 | * @param toScene The scene being entered when the given transition will |
| 95 | * be run |
| 96 | * @param transition The transition that will play when the given scene is |
| 97 | * entered. A value of null will result in the default behavior of |
| 98 | * using {@link AutoTransition}. |
| 99 | */ |
| 100 | public void setTransition(Scene fromScene, Scene toScene, Transition transition) { |
Chet Haase | 0873518 | 2013-06-04 10:44:40 -0700 | [diff] [blame] | 101 | ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene); |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 102 | if (sceneTransitionMap == null) { |
Chet Haase | 0873518 | 2013-06-04 10:44:40 -0700 | [diff] [blame] | 103 | sceneTransitionMap = new ArrayMap<Scene, Transition>(); |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 104 | mScenePairTransitions.put(toScene, sceneTransitionMap); |
| 105 | } |
| 106 | sceneTransitionMap.put(fromScene, transition); |
| 107 | } |
| 108 | |
| 109 | /** |
| 110 | * Returns the Transition for the given scene being entered. The result |
| 111 | * depends not only on the given scene, but also the scene which the |
| 112 | * {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in. |
| 113 | * |
| 114 | * @param scene The scene being entered |
| 115 | * @return The Transition to be used for the given scene change. If no |
| 116 | * Transition was specified for this scene change, {@link AutoTransition} |
| 117 | * will be used instead. |
| 118 | */ |
| 119 | private Transition getTransition(Scene scene) { |
| 120 | Transition transition = null; |
| 121 | ViewGroup sceneRoot = scene.getSceneRoot(); |
| 122 | if (sceneRoot != null) { |
| 123 | // TODO: cached in Scene instead? long-term, cache in View itself |
| 124 | Scene currScene = sceneRoot.getCurrentScene(); |
| 125 | if (currScene != null) { |
Chet Haase | 0873518 | 2013-06-04 10:44:40 -0700 | [diff] [blame] | 126 | ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(scene); |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 127 | if (sceneTransitionMap != null) { |
| 128 | transition = sceneTransitionMap.get(currScene); |
| 129 | if (transition != null) { |
| 130 | return transition; |
| 131 | } |
| 132 | } |
| 133 | } |
| 134 | } |
| 135 | transition = mSceneTransitions.get(scene); |
| 136 | return (transition != null) ? transition : new AutoTransition(); |
| 137 | } |
| 138 | |
| 139 | /** |
| 140 | * This is where all of the work of a transition/scene-change is |
| 141 | * orchestrated. This method captures the start values for the given |
| 142 | * transition, exits the current Scene, enters the new scene, captures |
| 143 | * the end values for the transition, and finally plays the |
| 144 | * resulting values-populated transition. |
| 145 | * |
| 146 | * @param scene The scene being entered |
| 147 | * @param transition The transition to play for this scene change |
| 148 | */ |
Chet Haase | 6ebe3de | 2013-06-17 16:50:50 -0700 | [diff] [blame] | 149 | private static void changeScene(Scene scene, Transition transition) { |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 150 | |
| 151 | final ViewGroup sceneRoot = scene.getSceneRoot(); |
| 152 | |
Chet Haase | 6ebe3de | 2013-06-17 16:50:50 -0700 | [diff] [blame] | 153 | Transition transitionClone = transition.clone(); |
| 154 | transitionClone.setSceneRoot(sceneRoot); |
| 155 | |
| 156 | sceneChangeSetup(sceneRoot, transitionClone); |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 157 | |
| 158 | scene.enter(); |
| 159 | |
Chet Haase | 6ebe3de | 2013-06-17 16:50:50 -0700 | [diff] [blame] | 160 | sceneChangeRunTransition(sceneRoot, transitionClone); |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 161 | } |
| 162 | |
| 163 | private static void sceneChangeRunTransition(final ViewGroup sceneRoot, |
| 164 | final Transition transition) { |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 165 | if (transition != null) { |
| 166 | final ViewTreeObserver observer = sceneRoot.getViewTreeObserver(); |
| 167 | observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() { |
| 168 | public boolean onPreDraw() { |
| 169 | sceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); |
Chet Haase | c43524f | 2013-07-16 14:40:11 -0700 | [diff] [blame] | 170 | sPendingTransitions.remove(sceneRoot); |
Chet Haase | e9d32ea | 2013-06-04 08:46:42 -0700 | [diff] [blame] | 171 | // Add to running list, handle end to remove it |
| 172 | sRunningTransitions.put(sceneRoot, transition); |
| 173 | transition.addListener(new Transition.TransitionListenerAdapter() { |
| 174 | @Override |
| 175 | public void onTransitionEnd(Transition transition) { |
| 176 | sRunningTransitions.remove(sceneRoot); |
| 177 | } |
| 178 | }); |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 179 | transition.captureValues(sceneRoot, false); |
Chet Haase | 6ebe3de | 2013-06-17 16:50:50 -0700 | [diff] [blame] | 180 | transition.playTransition(sceneRoot); |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 181 | return true; |
| 182 | } |
| 183 | }); |
| 184 | } |
| 185 | } |
| 186 | |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 187 | private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) { |
| 188 | |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 189 | // Capture current values |
Chet Haase | c81a849 | 2013-07-12 12:54:38 -0700 | [diff] [blame] | 190 | Transition runningTransition = sRunningTransitions.get(sceneRoot); |
| 191 | |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 192 | if (transition != null) { |
| 193 | transition.captureValues(sceneRoot, true); |
| 194 | } |
| 195 | |
Chet Haase | c81a849 | 2013-07-12 12:54:38 -0700 | [diff] [blame] | 196 | if (runningTransition != null) { |
| 197 | runningTransition.cancelTransition(); |
| 198 | } |
| 199 | |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 200 | // Notify previous scene that it is being exited |
| 201 | Scene previousScene = sceneRoot.getCurrentScene(); |
| 202 | if (previousScene != null) { |
| 203 | previousScene.exit(); |
| 204 | } |
| 205 | } |
| 206 | |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 207 | /** |
| 208 | * Change to the given scene, using the |
| 209 | * appropriate transition for this particular scene change |
| 210 | * (as specified to the TransitionManager, or the default |
| 211 | * if no such transition exists). |
| 212 | * |
| 213 | * @param scene The Scene to change to |
| 214 | */ |
| 215 | public void transitionTo(Scene scene) { |
| 216 | // Auto transition if there is no transition declared for the Scene, but there is |
| 217 | // a root or parent view |
| 218 | changeScene(scene, getTransition(scene)); |
| 219 | |
| 220 | } |
| 221 | |
| 222 | /** |
| 223 | * Static utility method to simply change to the given scene using |
| 224 | * the default transition for TransitionManager. |
| 225 | * |
| 226 | * @param scene The Scene to change to |
| 227 | */ |
| 228 | public static void go(Scene scene) { |
| 229 | changeScene(scene, sDefaultTransition); |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * Static utility method to simply change to the given scene using |
| 234 | * the given transition. |
| 235 | * |
| 236 | * <p>Passing in <code>null</code> for the transition parameter will |
| 237 | * result in the scene changing without any transition running, and is |
| 238 | * equivalent to calling {@link Scene#exit()} on the scene root's |
| 239 | * {@link ViewGroup#getCurrentScene() current scene}, followed by |
| 240 | * {@link Scene#enter()} on the scene specified by the <code>scene</code> |
| 241 | * parameter.</p> |
| 242 | * |
| 243 | * @param scene The Scene to change to |
| 244 | * @param transition The transition to use for this scene change. A |
| 245 | * value of null causes the scene change to happen with no transition. |
| 246 | */ |
| 247 | public static void go(Scene scene, Transition transition) { |
| 248 | changeScene(scene, transition); |
| 249 | } |
| 250 | |
| 251 | /** |
| 252 | * Static utility method to simply change to a scene defined by the |
| 253 | * code in the given runnable, which will be executed after |
| 254 | * the current values have been captured for the transition. |
| 255 | * This is equivalent to creating a Scene and calling {@link |
| 256 | * Scene#setEnterAction(Runnable)} with the runnable, then calling |
| 257 | * {@link #go(Scene, Transition)}. The transition used will be the |
| 258 | * default provided by TransitionManager. |
| 259 | * |
| 260 | * @param sceneRoot The root of the View hierarchy used when this scene |
| 261 | * runs a transition automatically. |
| 262 | * @param action The runnable whose {@link Runnable#run() run()} method will |
| 263 | * be called. |
| 264 | */ |
| 265 | public static void go(ViewGroup sceneRoot, Runnable action) { |
| 266 | Scene scene = new Scene(sceneRoot); |
| 267 | scene.setEnterAction(action); |
| 268 | changeScene(scene, sDefaultTransition); |
| 269 | } |
| 270 | |
| 271 | /** |
| 272 | * Static utility method to simply change to a scene defined by the |
| 273 | * code in the given runnable, which will be executed after |
| 274 | * the current values have been captured for the transition. |
| 275 | * This is equivalent to creating a Scene and calling {@link |
| 276 | * Scene#setEnterAction(Runnable)} with the runnable, then calling |
| 277 | * {@link #go(Scene, Transition)}. The given transition will be |
| 278 | * used to animate the changes. |
| 279 | * |
| 280 | * <p>Passing in <code>null</code> for the transition parameter will |
| 281 | * result in the scene changing without any transition running, and is |
| 282 | * equivalent to calling {@link Scene#exit()} on the scene root's |
| 283 | * {@link ViewGroup#getCurrentScene() current scene}, followed by |
| 284 | * {@link Scene#enter()} on a new scene specified by the |
| 285 | * <code>action</code> parameter.</p> |
| 286 | * |
| 287 | * @param sceneRoot The root of the View hierarchy to run the transition on. |
| 288 | * @param action The runnable whose {@link Runnable#run() run()} method will |
| 289 | * be called. |
| 290 | * @param transition The transition to use for this change. A |
| 291 | * value of null causes the change to happen with no transition. |
| 292 | */ |
| 293 | public static void go(ViewGroup sceneRoot, Runnable action, Transition transition) { |
| 294 | Scene scene = new Scene(sceneRoot); |
| 295 | scene.setEnterAction(action); |
| 296 | changeScene(scene, transition); |
| 297 | } |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 298 | |
| 299 | /** |
| 300 | * Static utility method to animate to a new scene defined by all changes within |
| 301 | * the given scene root between calling this method and the next rendering frame. |
| 302 | * Calling this method causes TransitionManager to capture current values in the |
| 303 | * scene root and then post a request to run a transition on the next frame. |
| 304 | * At that time, the new values in the scene root will be captured and changes |
| 305 | * will be animated. There is no need to create a Scene; it is implied by |
| 306 | * changes which take place between calling this method and the next frame when |
| 307 | * the transition begins. |
| 308 | * |
| 309 | * <p>Calling this method several times before the next frame (for example, if |
| 310 | * unrelated code also wants to make dynamic changes and run a transition on |
| 311 | * the same scene root), only the first call will trigger capturing values |
| 312 | * and exiting the current scene. Subsequent calls to the method with the |
| 313 | * same scene root during the same frame will be ignored.</p> |
| 314 | * |
| 315 | * <p>Passing in <code>null</code> for the transition parameter will |
| 316 | * cause the TransitionManager to use its default transition.</p> |
| 317 | * |
| 318 | * @param sceneRoot The root of the View hierarchy to run the transition on. |
| 319 | * @param transition The transition to use for this change. A |
| 320 | * value of null causes the TransitionManager to use the default transition. |
| 321 | */ |
| 322 | public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) { |
Chet Haase | 7a46dde | 2013-07-17 10:22:53 -0700 | [diff] [blame] | 323 | if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) { |
Chet Haase | c43524f | 2013-07-16 14:40:11 -0700 | [diff] [blame] | 324 | if (Transition.DBG) { |
| 325 | Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " + |
| 326 | sceneRoot + ", " + transition); |
| 327 | } |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 328 | sPendingTransitions.add(sceneRoot); |
| 329 | if (transition == null) { |
| 330 | transition = sDefaultTransition; |
| 331 | } |
Chet Haase | 6ebe3de | 2013-06-17 16:50:50 -0700 | [diff] [blame] | 332 | final Transition finalTransition = transition.clone(); |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 333 | sceneChangeSetup(sceneRoot, transition); |
| 334 | sceneRoot.setCurrentScene(null); |
Chet Haase | c43524f | 2013-07-16 14:40:11 -0700 | [diff] [blame] | 335 | sceneChangeRunTransition(sceneRoot, finalTransition); |
Chet Haase | 4f50723 | 2013-06-10 13:00:06 -0700 | [diff] [blame] | 336 | } |
| 337 | } |
Chet Haase | faebd8f | 2012-05-18 14:17:57 -0700 | [diff] [blame] | 338 | } |