1faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/*
2faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Copyright (C) 2013 The Android Open Source Project
3faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *
4faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Licensed under the Apache License, Version 2.0 (the "License");
5faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * you may not use this file except in compliance with the License.
6faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * You may obtain a copy of the License at
7faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *
8faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *      http://www.apache.org/licenses/LICENSE-2.0
9faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase *
10faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Unless required by applicable law or agreed to in writing, software
11faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * distributed under the License is distributed on an "AS IS" BASIS,
12faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * See the License for the specific language governing permissions and
14faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * limitations under the License.
15faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */
166ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase
17d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haasepackage android.transition;
18faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
19d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haaseimport android.content.Context;
20e9d32ea13ee14fc0eb4e45ca627ca77729d38bfeChet Haaseimport android.util.ArrayMap;
21c43524f3869cc0d36974fce61986017093f2ecd2Chet Haaseimport android.util.Log;
22df32aa87150768795816852c6393306893467ecaChet Haaseimport android.view.View;
23faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.ViewGroup;
24faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haaseimport android.view.ViewTreeObserver;
25faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
267660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haaseimport java.lang.ref.WeakReference;
274f5072327d00822a2bfaff56df46cea2981ac90dChet Haaseimport java.util.ArrayList;
284f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
29faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase/**
30faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * This class manages the set of transitions that fire when there is a
31faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * change of {@link Scene}. To use the manager, add scenes along with
32faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * transition objects with calls to {@link #setTransition(Scene, Transition)}
33faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * or {@link #setTransition(Scene, Scene, Transition)}. Setting specific
34faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * transitions for scene changes is not required; by default, a Scene change
35faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * will use {@link AutoTransition} to do something reasonable for most
36faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * situations. Specifying other transitions for particular scene changes is
37faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * only necessary if the application wants different transition behavior
38faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * in these situations.
39d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase *
40d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <p>TransitionManagers can be declared in XML resource files inside the
41d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <code>res/transition</code> directory. TransitionManager resources consist of
42d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * the <code>transitionManager</code>tag name, containing one or more
43d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <code>transition</code> tags, each of which describe the relationship of
44d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * that transition to the from/to scene information in that tag.
45d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * For example, here is a resource file that declares several scene
46d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * transitions:</p>
47d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase *
48d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * {@sample development/samples/ApiDemos/res/transition/transitions_mgr.xml TransitionManager}
49d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase *
50d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <p>For each of the <code>fromScene</code> and <code>toScene</code> attributes,
51d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * there is a reference to a standard XML layout file. This is equivalent to
52d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * creating a scene from a layout in code by calling
53d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * {@link Scene#getSceneForLayout(ViewGroup, int, Context)}. For the
54d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * <code>transition</code> attribute, there is a reference to a resource
55d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * file in the <code>res/transition</code> directory which describes that
56d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * transition.</p>
57d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase *
58d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * Information on XML resource descriptions for transitions can be found for
59d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
60d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
61d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * and {@link android.R.styleable#TransitionManager}.
62faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */
63faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haasepublic class TransitionManager {
64faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    // TODO: how to handle enter/exit?
65faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
66c43524f3869cc0d36974fce61986017093f2ecd2Chet Haase    private static String LOG_TAG = "TransitionManager";
67c43524f3869cc0d36974fce61986017093f2ecd2Chet Haase
68d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    private static Transition sDefaultTransition = new AutoTransition();
69faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
70cfbe9be5b3b701d95fb24fa0f7c8d9be43eec776Adam Powell    private static final String[] EMPTY_STRINGS = new String[0];
71cfbe9be5b3b701d95fb24fa0f7c8d9be43eec776Adam Powell
7208735185f8105710e18ad02297461bec9268e514Chet Haase    ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>();
7308735185f8105710e18ad02297461bec9268e514Chet Haase    ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions =
7408735185f8105710e18ad02297461bec9268e514Chet Haase            new ArrayMap<Scene, ArrayMap<Scene, Transition>>();
757660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase    private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>
767660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            sRunningTransitions =
777660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            new ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>();
784f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>();
794f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
80faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
814f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    /**
82faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Sets the transition to be used for any scene change for which no
83faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * other transition is explicitly set. The initial value is
84faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * an {@link AutoTransition} instance.
85faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
86faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The default transition to be used for scene changes.
871e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     *
881e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * @hide pending later changes
89faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
90faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setDefaultTransition(Transition transition) {
91d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        sDefaultTransition = transition;
92faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
93faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
94faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
95faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Gets the current default transition. The initial value is an {@link
96faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * AutoTransition} instance.
97faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
98faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @return The current default transition.
99faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @see #setDefaultTransition(Transition)
1001e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     *
1011e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * @hide pending later changes
102faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
103d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public static Transition getDefaultTransition() {
104d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        return sDefaultTransition;
105faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
106faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
107faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
108faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Sets a specific transition to occur when the given scene is entered.
109faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
110faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The scene which, when applied, will cause the given
111faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * transition to run.
112faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition that will play when the given scene is
113faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * entered. A value of null will result in the default behavior of
1141e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * using the default transition instead.
115faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
116faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setTransition(Scene scene, Transition transition) {
117faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        mSceneTransitions.put(scene, transition);
118faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
119faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
120faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
121faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Sets a specific transition to occur when the given pair of scenes is
122faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * exited/entered.
123faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
124faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param fromScene The scene being exited when the given transition will
125faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * be run
126faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param toScene The scene being entered when the given transition will
127faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * be run
128faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition that will play when the given scene is
129faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * entered. A value of null will result in the default behavior of
1301e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * using the default transition instead.
131faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
132faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
13308735185f8105710e18ad02297461bec9268e514Chet Haase        ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
134faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (sceneTransitionMap == null) {
13508735185f8105710e18ad02297461bec9268e514Chet Haase            sceneTransitionMap = new ArrayMap<Scene, Transition>();
136faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            mScenePairTransitions.put(toScene, sceneTransitionMap);
137faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
138faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        sceneTransitionMap.put(fromScene, transition);
139faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
140faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
141faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
142faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Returns the Transition for the given scene being entered. The result
143faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * depends not only on the given scene, but also the scene which the
144faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in.
145faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
146faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The scene being entered
147faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @return The Transition to be used for the given scene change. If no
1481e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * Transition was specified for this scene change, the default transition
1491e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * will be used instead.
150faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
151faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private Transition getTransition(Scene scene) {
152faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        Transition transition = null;
153faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        ViewGroup sceneRoot = scene.getSceneRoot();
154faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (sceneRoot != null) {
155faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            // TODO: cached in Scene instead? long-term, cache in View itself
156d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase            Scene currScene = Scene.getCurrentScene(sceneRoot);
157faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            if (currScene != null) {
15808735185f8105710e18ad02297461bec9268e514Chet Haase                ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(scene);
159faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                if (sceneTransitionMap != null) {
160faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    transition = sceneTransitionMap.get(currScene);
161faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (transition != null) {
162faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        return transition;
163faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
164faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                }
165faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
166faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
167faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        transition = mSceneTransitions.get(scene);
168d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        return (transition != null) ? transition : sDefaultTransition;
169faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
170faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
171faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
172faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * This is where all of the work of a transition/scene-change is
173faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * orchestrated. This method captures the start values for the given
174faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * transition, exits the current Scene, enters the new scene, captures
175faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the end values for the transition, and finally plays the
176faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * resulting values-populated transition.
177faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
178faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The scene being entered
179faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition to play for this scene change
180faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
1816ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase    private static void changeScene(Scene scene, Transition transition) {
182faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
183faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        final ViewGroup sceneRoot = scene.getSceneRoot();
184faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
1855a7cf3eeeb208c2dac52541cb09b519b4342a5ffChet Haase        Transition transitionClone = null;
1865a7cf3eeeb208c2dac52541cb09b519b4342a5ffChet Haase        if (transition != null) {
1875a7cf3eeeb208c2dac52541cb09b519b4342a5ffChet Haase            transitionClone = transition.clone();
1885a7cf3eeeb208c2dac52541cb09b519b4342a5ffChet Haase            transitionClone.setSceneRoot(sceneRoot);
1895a7cf3eeeb208c2dac52541cb09b519b4342a5ffChet Haase        }
1906ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase
191b7a7fc9d233bad507ce893882352618b13647058Chet Haase        Scene oldScene = Scene.getCurrentScene(sceneRoot);
1925a7cf3eeeb208c2dac52541cb09b519b4342a5ffChet Haase        if (oldScene != null && transitionClone != null &&
1935a7cf3eeeb208c2dac52541cb09b519b4342a5ffChet Haase                oldScene.isCreatedFromLayoutResource()) {
194b7a7fc9d233bad507ce893882352618b13647058Chet Haase            transitionClone.setCanRemoveViews(true);
195b7a7fc9d233bad507ce893882352618b13647058Chet Haase        }
196b7a7fc9d233bad507ce893882352618b13647058Chet Haase
1976ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase        sceneChangeSetup(sceneRoot, transitionClone);
198faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
199faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        scene.enter();
200faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
2016ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase        sceneChangeRunTransition(sceneRoot, transitionClone);
2024f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    }
2034f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
204199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
2057660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase        WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
206199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                sRunningTransitions.get();
2077660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase        if (runningTransitions == null || runningTransitions.get() == null) {
2087660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            ArrayMap<ViewGroup, ArrayList<Transition>> transitions =
2097660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase                    new ArrayMap<ViewGroup, ArrayList<Transition>>();
2107660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            runningTransitions = new WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>(
2117660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase                    transitions);
212199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            sRunningTransitions.set(runningTransitions);
213199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        }
2147660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase        return runningTransitions.get();
215199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    }
216199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
2174f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
2184f5072327d00822a2bfaff56df46cea2981ac90dChet Haase            final Transition transition) {
219df32aa87150768795816852c6393306893467ecaChet Haase        if (transition != null && sceneRoot != null) {
220df32aa87150768795816852c6393306893467ecaChet Haase            MultiListener listener = new MultiListener(transition, sceneRoot);
221df32aa87150768795816852c6393306893467ecaChet Haase            sceneRoot.addOnAttachStateChangeListener(listener);
222df32aa87150768795816852c6393306893467ecaChet Haase            sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
223df32aa87150768795816852c6393306893467ecaChet Haase        }
224df32aa87150768795816852c6393306893467ecaChet Haase    }
225df32aa87150768795816852c6393306893467ecaChet Haase
226df32aa87150768795816852c6393306893467ecaChet Haase    /**
227df32aa87150768795816852c6393306893467ecaChet Haase     * This private utility class is used to listen for both OnPreDraw and
228df32aa87150768795816852c6393306893467ecaChet Haase     * OnAttachStateChange events. OnPreDraw events are the main ones we care
229df32aa87150768795816852c6393306893467ecaChet Haase     * about since that's what triggers the transition to take place.
230df32aa87150768795816852c6393306893467ecaChet Haase     * OnAttachStateChange events are also important in case the view is removed
231df32aa87150768795816852c6393306893467ecaChet Haase     * from the hierarchy before the OnPreDraw event takes place; it's used to
232df32aa87150768795816852c6393306893467ecaChet Haase     * clean up things since the OnPreDraw listener didn't get called in time.
233df32aa87150768795816852c6393306893467ecaChet Haase     */
234df32aa87150768795816852c6393306893467ecaChet Haase    private static class MultiListener implements ViewTreeObserver.OnPreDrawListener,
235df32aa87150768795816852c6393306893467ecaChet Haase            View.OnAttachStateChangeListener {
236df32aa87150768795816852c6393306893467ecaChet Haase
237df32aa87150768795816852c6393306893467ecaChet Haase        Transition mTransition;
238df32aa87150768795816852c6393306893467ecaChet Haase        ViewGroup mSceneRoot;
239df32aa87150768795816852c6393306893467ecaChet Haase
240df32aa87150768795816852c6393306893467ecaChet Haase        MultiListener(Transition transition, ViewGroup sceneRoot) {
241df32aa87150768795816852c6393306893467ecaChet Haase            mTransition = transition;
242df32aa87150768795816852c6393306893467ecaChet Haase            mSceneRoot = sceneRoot;
243df32aa87150768795816852c6393306893467ecaChet Haase        }
244df32aa87150768795816852c6393306893467ecaChet Haase
245df32aa87150768795816852c6393306893467ecaChet Haase        private void removeListeners() {
246df32aa87150768795816852c6393306893467ecaChet Haase            mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
247df32aa87150768795816852c6393306893467ecaChet Haase            mSceneRoot.removeOnAttachStateChangeListener(this);
248df32aa87150768795816852c6393306893467ecaChet Haase        }
249199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
250df32aa87150768795816852c6393306893467ecaChet Haase        @Override
251df32aa87150768795816852c6393306893467ecaChet Haase        public void onViewAttachedToWindow(View v) {
252df32aa87150768795816852c6393306893467ecaChet Haase        }
253df32aa87150768795816852c6393306893467ecaChet Haase
254df32aa87150768795816852c6393306893467ecaChet Haase        @Override
255df32aa87150768795816852c6393306893467ecaChet Haase        public void onViewDetachedFromWindow(View v) {
256df32aa87150768795816852c6393306893467ecaChet Haase            removeListeners();
257df32aa87150768795816852c6393306893467ecaChet Haase
258df32aa87150768795816852c6393306893467ecaChet Haase            sPendingTransitions.remove(mSceneRoot);
259df32aa87150768795816852c6393306893467ecaChet Haase            ArrayList<Transition> runningTransitions = getRunningTransitions().get(mSceneRoot);
260df32aa87150768795816852c6393306893467ecaChet Haase            if (runningTransitions != null && runningTransitions.size() > 0) {
261df32aa87150768795816852c6393306893467ecaChet Haase                for (Transition runningTransition : runningTransitions) {
262cf68aad3164303df59b2a669d186a94533c9c743George Mount                    runningTransition.resume(mSceneRoot);
263faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                }
264df32aa87150768795816852c6393306893467ecaChet Haase            }
265df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.clearValues(true);
266faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
267df32aa87150768795816852c6393306893467ecaChet Haase
268df32aa87150768795816852c6393306893467ecaChet Haase        @Override
269df32aa87150768795816852c6393306893467ecaChet Haase        public boolean onPreDraw() {
270df32aa87150768795816852c6393306893467ecaChet Haase            removeListeners();
271df32aa87150768795816852c6393306893467ecaChet Haase            sPendingTransitions.remove(mSceneRoot);
272df32aa87150768795816852c6393306893467ecaChet Haase            // Add to running list, handle end to remove it
273df32aa87150768795816852c6393306893467ecaChet Haase            final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
274df32aa87150768795816852c6393306893467ecaChet Haase                    getRunningTransitions();
275df32aa87150768795816852c6393306893467ecaChet Haase            ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
276df32aa87150768795816852c6393306893467ecaChet Haase            ArrayList<Transition> previousRunningTransitions = null;
277df32aa87150768795816852c6393306893467ecaChet Haase            if (currentTransitions == null) {
278df32aa87150768795816852c6393306893467ecaChet Haase                currentTransitions = new ArrayList<Transition>();
279df32aa87150768795816852c6393306893467ecaChet Haase                runningTransitions.put(mSceneRoot, currentTransitions);
280df32aa87150768795816852c6393306893467ecaChet Haase            } else if (currentTransitions.size() > 0) {
281df32aa87150768795816852c6393306893467ecaChet Haase                previousRunningTransitions = new ArrayList<Transition>(currentTransitions);
282df32aa87150768795816852c6393306893467ecaChet Haase            }
283df32aa87150768795816852c6393306893467ecaChet Haase            currentTransitions.add(mTransition);
284df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.addListener(new Transition.TransitionListenerAdapter() {
285df32aa87150768795816852c6393306893467ecaChet Haase                @Override
286df32aa87150768795816852c6393306893467ecaChet Haase                public void onTransitionEnd(Transition transition) {
287df32aa87150768795816852c6393306893467ecaChet Haase                    ArrayList<Transition> currentTransitions =
288df32aa87150768795816852c6393306893467ecaChet Haase                            runningTransitions.get(mSceneRoot);
289df32aa87150768795816852c6393306893467ecaChet Haase                    currentTransitions.remove(transition);
290df32aa87150768795816852c6393306893467ecaChet Haase                }
291df32aa87150768795816852c6393306893467ecaChet Haase            });
292df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.captureValues(mSceneRoot, false);
293df32aa87150768795816852c6393306893467ecaChet Haase            if (previousRunningTransitions != null) {
294df32aa87150768795816852c6393306893467ecaChet Haase                for (Transition runningTransition : previousRunningTransitions) {
295cf68aad3164303df59b2a669d186a94533c9c743George Mount                    runningTransition.resume(mSceneRoot);
296df32aa87150768795816852c6393306893467ecaChet Haase                }
297df32aa87150768795816852c6393306893467ecaChet Haase            }
298df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.playTransition(mSceneRoot);
299df32aa87150768795816852c6393306893467ecaChet Haase
300df32aa87150768795816852c6393306893467ecaChet Haase            return true;
301df32aa87150768795816852c6393306893467ecaChet Haase        }
302df32aa87150768795816852c6393306893467ecaChet Haase    };
303faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
3044f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
3054f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
306c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase        // Capture current values
307199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
3084f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
309199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        if (runningTransitions != null && runningTransitions.size() > 0) {
310199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            for (Transition runningTransition : runningTransitions) {
311cf68aad3164303df59b2a669d186a94533c9c743George Mount                runningTransition.pause(sceneRoot);
312199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            }
3134f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        }
3144f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
315199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        if (transition != null) {
316199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            transition.captureValues(sceneRoot, true);
317c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase        }
318c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase
3194f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        // Notify previous scene that it is being exited
320d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        Scene previousScene = Scene.getCurrentScene(sceneRoot);
3214f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        if (previousScene != null) {
3224f5072327d00822a2bfaff56df46cea2981ac90dChet Haase            previousScene.exit();
3234f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        }
3244f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    }
3254f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
326faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
327faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Change to the given scene, using the
328faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * appropriate transition for this particular scene change
329faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * (as specified to the TransitionManager, or the default
330faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * if no such transition exists).
331faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
332faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The Scene to change to
333faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
334faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void transitionTo(Scene scene) {
335faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        // Auto transition if there is no transition declared for the Scene, but there is
336faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        // a root or parent view
337faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        changeScene(scene, getTransition(scene));
338faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
339faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
340faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
341d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to simply change to the given scene using
342faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the default transition for TransitionManager.
343faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
344faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The Scene to change to
345faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
346faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public static void go(Scene scene) {
347faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        changeScene(scene, sDefaultTransition);
348faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
349faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
350faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
351d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to simply change to the given scene using
352faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the given transition.
353faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
354faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * <p>Passing in <code>null</code> for the transition parameter will
355faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * result in the scene changing without any transition running, and is
356faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * equivalent to calling {@link Scene#exit()} on the scene root's
357d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * current scene, followed by {@link Scene#enter()} on the scene
358d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * specified by the <code>scene</code> parameter.</p>
359faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
360faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The Scene to change to
361faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition to use for this scene change. A
362faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * value of null causes the scene change to happen with no transition.
363faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
364faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public static void go(Scene scene, Transition transition) {
365faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        changeScene(scene, transition);
366faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
367faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
368faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
369d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to animate, using the default transition,
370d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * to a new scene defined by all changes within the given scene root between
371d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * calling this method and the next rendering frame.
372d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Equivalent to calling {@link #beginDelayedTransition(ViewGroup, Transition)}
373d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * with a value of <code>null</code> for the <code>transition</code> parameter.
374faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
375faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param sceneRoot The root of the View hierarchy to run the transition on.
376faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
377d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public static void beginDelayedTransition(final ViewGroup sceneRoot) {
378d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        beginDelayedTransition(sceneRoot, null);
379faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
3804f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
3814f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    /**
382d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to animate to a new scene defined by all changes within
3834f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * the given scene root between calling this method and the next rendering frame.
3844f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * Calling this method causes TransitionManager to capture current values in the
3854f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * scene root and then post a request to run a transition on the next frame.
3864f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * At that time, the new values in the scene root will be captured and changes
3874f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * will be animated. There is no need to create a Scene; it is implied by
3884f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * changes which take place between calling this method and the next frame when
3894f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * the transition begins.
3904f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     *
3914f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * <p>Calling this method several times before the next frame (for example, if
3924f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * unrelated code also wants to make dynamic changes and run a transition on
3934f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * the same scene root), only the first call will trigger capturing values
3944f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * and exiting the current scene. Subsequent calls to the method with the
3954f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * same scene root during the same frame will be ignored.</p>
3964f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     *
3974f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * <p>Passing in <code>null</code> for the transition parameter will
3984f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * cause the TransitionManager to use its default transition.</p>
3994f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     *
4004f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * @param sceneRoot The root of the View hierarchy to run the transition on.
4014f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * @param transition The transition to use for this change. A
4024f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * value of null causes the TransitionManager to use the default transition.
4034f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     */
4044f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
40523c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase        if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) {
40623c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            if (Transition.DBG) {
40723c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase                Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
40823c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase                        sceneRoot + ", " + transition);
40923c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            }
41023c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            sPendingTransitions.add(sceneRoot);
41123c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            if (transition == null) {
41223c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase                transition = sDefaultTransition;
41323c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            }
41423c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            final Transition transitionClone = transition.clone();
41523c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            sceneChangeSetup(sceneRoot, transitionClone);
41623c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            Scene.setCurrentScene(sceneRoot, null);
41723c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            sceneChangeRunTransition(sceneRoot, transitionClone);
41823c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase        }
4194f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    }
420faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase}
421