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
7008735185f8105710e18ad02297461bec9268e514Chet Haase    ArrayMap<Scene, Transition> mSceneTransitions = new ArrayMap<Scene, Transition>();
7108735185f8105710e18ad02297461bec9268e514Chet Haase    ArrayMap<Scene, ArrayMap<Scene, Transition>> mScenePairTransitions =
7208735185f8105710e18ad02297461bec9268e514Chet Haase            new ArrayMap<Scene, ArrayMap<Scene, Transition>>();
737660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase    private static ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>
747660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            sRunningTransitions =
757660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            new ThreadLocal<WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>>();
764f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    private static ArrayList<ViewGroup> sPendingTransitions = new ArrayList<ViewGroup>();
774f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
78faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
794f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    /**
80faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Sets the transition to be used for any scene change for which no
81faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * other transition is explicitly set. The initial value is
82faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * an {@link AutoTransition} instance.
83faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
84faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The default transition to be used for scene changes.
851e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     *
861e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * @hide pending later changes
87faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
88faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setDefaultTransition(Transition transition) {
89d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        sDefaultTransition = transition;
90faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
91faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
92faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
93faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Gets the current default transition. The initial value is an {@link
94faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * AutoTransition} instance.
95faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
96faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @return The current default transition.
97faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @see #setDefaultTransition(Transition)
981e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     *
991e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * @hide pending later changes
100faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
101d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public static Transition getDefaultTransition() {
102d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        return sDefaultTransition;
103faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
104faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
105faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
106faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Sets a specific transition to occur when the given scene is entered.
107faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
108faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The scene which, when applied, will cause the given
109faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * transition to run.
110faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition that will play when the given scene is
111faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * entered. A value of null will result in the default behavior of
1121e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * using the default transition instead.
113faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
114faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setTransition(Scene scene, Transition transition) {
115faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        mSceneTransitions.put(scene, transition);
116faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
117faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
118faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
119faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Sets a specific transition to occur when the given pair of scenes is
120faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * exited/entered.
121faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
122faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param fromScene The scene being exited when the given transition will
123faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * be run
124faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param toScene The scene being entered when the given transition will
125faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * be run
126faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition that will play when the given scene is
127faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * entered. A value of null will result in the default behavior of
1281e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * using the default transition instead.
129faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
130faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void setTransition(Scene fromScene, Scene toScene, Transition transition) {
13108735185f8105710e18ad02297461bec9268e514Chet Haase        ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(toScene);
132faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (sceneTransitionMap == null) {
13308735185f8105710e18ad02297461bec9268e514Chet Haase            sceneTransitionMap = new ArrayMap<Scene, Transition>();
134faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            mScenePairTransitions.put(toScene, sceneTransitionMap);
135faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
136faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        sceneTransitionMap.put(fromScene, transition);
137faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
138faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
139faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
140faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Returns the Transition for the given scene being entered. The result
141faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * depends not only on the given scene, but also the scene which the
142faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * {@link Scene#getSceneRoot() sceneRoot} of the Scene is currently in.
143faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
144faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The scene being entered
145faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @return The Transition to be used for the given scene change. If no
1461e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * Transition was specified for this scene change, the default transition
1471e9f3d868bab573072dcfa28d3b3f984de5a6756Adam Powell     * will be used instead.
148faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
149faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    private Transition getTransition(Scene scene) {
150faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        Transition transition = null;
151faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        ViewGroup sceneRoot = scene.getSceneRoot();
152faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        if (sceneRoot != null) {
153faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            // TODO: cached in Scene instead? long-term, cache in View itself
154d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase            Scene currScene = Scene.getCurrentScene(sceneRoot);
155faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            if (currScene != null) {
15608735185f8105710e18ad02297461bec9268e514Chet Haase                ArrayMap<Scene, Transition> sceneTransitionMap = mScenePairTransitions.get(scene);
157faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                if (sceneTransitionMap != null) {
158faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    transition = sceneTransitionMap.get(currScene);
159faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    if (transition != null) {
160faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                        return transition;
161faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                    }
162faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                }
163faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase            }
164faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
165faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        transition = mSceneTransitions.get(scene);
166d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        return (transition != null) ? transition : sDefaultTransition;
167faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
168faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
169faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
170faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * This is where all of the work of a transition/scene-change is
171faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * orchestrated. This method captures the start values for the given
172faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * transition, exits the current Scene, enters the new scene, captures
173faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the end values for the transition, and finally plays the
174faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * resulting values-populated transition.
175faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
176faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The scene being entered
177faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition to play for this scene change
178faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
1796ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase    private static void changeScene(Scene scene, Transition transition) {
180faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
181faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        final ViewGroup sceneRoot = scene.getSceneRoot();
182faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
1836ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase        Transition transitionClone = transition.clone();
1846ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase        transitionClone.setSceneRoot(sceneRoot);
1856ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase
186b7a7fc9d233bad507ce893882352618b13647058Chet Haase        Scene oldScene = Scene.getCurrentScene(sceneRoot);
187b7a7fc9d233bad507ce893882352618b13647058Chet Haase        if (oldScene != null && oldScene.isCreatedFromLayoutResource()) {
188b7a7fc9d233bad507ce893882352618b13647058Chet Haase            transitionClone.setCanRemoveViews(true);
189b7a7fc9d233bad507ce893882352618b13647058Chet Haase        }
190b7a7fc9d233bad507ce893882352618b13647058Chet Haase
1916ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase        sceneChangeSetup(sceneRoot, transitionClone);
192faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
193faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        scene.enter();
194faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
1956ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase        sceneChangeRunTransition(sceneRoot, transitionClone);
1964f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    }
1974f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
198199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() {
1997660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase        WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions =
200199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                sRunningTransitions.get();
2017660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase        if (runningTransitions == null || runningTransitions.get() == null) {
2027660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            ArrayMap<ViewGroup, ArrayList<Transition>> transitions =
2037660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase                    new ArrayMap<ViewGroup, ArrayList<Transition>>();
2047660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase            runningTransitions = new WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>(
2057660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase                    transitions);
206199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            sRunningTransitions.set(runningTransitions);
207199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        }
2087660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase        return runningTransitions.get();
209199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase    }
210199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
2114f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    private static void sceneChangeRunTransition(final ViewGroup sceneRoot,
2124f5072327d00822a2bfaff56df46cea2981ac90dChet Haase            final Transition transition) {
213df32aa87150768795816852c6393306893467ecaChet Haase        if (transition != null && sceneRoot != null) {
214df32aa87150768795816852c6393306893467ecaChet Haase            MultiListener listener = new MultiListener(transition, sceneRoot);
215df32aa87150768795816852c6393306893467ecaChet Haase            sceneRoot.addOnAttachStateChangeListener(listener);
216df32aa87150768795816852c6393306893467ecaChet Haase            sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener);
217df32aa87150768795816852c6393306893467ecaChet Haase        }
218df32aa87150768795816852c6393306893467ecaChet Haase    }
219df32aa87150768795816852c6393306893467ecaChet Haase
220df32aa87150768795816852c6393306893467ecaChet Haase    /**
221df32aa87150768795816852c6393306893467ecaChet Haase     * This private utility class is used to listen for both OnPreDraw and
222df32aa87150768795816852c6393306893467ecaChet Haase     * OnAttachStateChange events. OnPreDraw events are the main ones we care
223df32aa87150768795816852c6393306893467ecaChet Haase     * about since that's what triggers the transition to take place.
224df32aa87150768795816852c6393306893467ecaChet Haase     * OnAttachStateChange events are also important in case the view is removed
225df32aa87150768795816852c6393306893467ecaChet Haase     * from the hierarchy before the OnPreDraw event takes place; it's used to
226df32aa87150768795816852c6393306893467ecaChet Haase     * clean up things since the OnPreDraw listener didn't get called in time.
227df32aa87150768795816852c6393306893467ecaChet Haase     */
228df32aa87150768795816852c6393306893467ecaChet Haase    private static class MultiListener implements ViewTreeObserver.OnPreDrawListener,
229df32aa87150768795816852c6393306893467ecaChet Haase            View.OnAttachStateChangeListener {
230df32aa87150768795816852c6393306893467ecaChet Haase
231df32aa87150768795816852c6393306893467ecaChet Haase        Transition mTransition;
232df32aa87150768795816852c6393306893467ecaChet Haase        ViewGroup mSceneRoot;
233df32aa87150768795816852c6393306893467ecaChet Haase
234df32aa87150768795816852c6393306893467ecaChet Haase        MultiListener(Transition transition, ViewGroup sceneRoot) {
235df32aa87150768795816852c6393306893467ecaChet Haase            mTransition = transition;
236df32aa87150768795816852c6393306893467ecaChet Haase            mSceneRoot = sceneRoot;
237df32aa87150768795816852c6393306893467ecaChet Haase        }
238df32aa87150768795816852c6393306893467ecaChet Haase
239df32aa87150768795816852c6393306893467ecaChet Haase        private void removeListeners() {
240df32aa87150768795816852c6393306893467ecaChet Haase            mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this);
241df32aa87150768795816852c6393306893467ecaChet Haase            mSceneRoot.removeOnAttachStateChangeListener(this);
242df32aa87150768795816852c6393306893467ecaChet Haase        }
243199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase
244df32aa87150768795816852c6393306893467ecaChet Haase        @Override
245df32aa87150768795816852c6393306893467ecaChet Haase        public void onViewAttachedToWindow(View v) {
246df32aa87150768795816852c6393306893467ecaChet Haase        }
247df32aa87150768795816852c6393306893467ecaChet Haase
248df32aa87150768795816852c6393306893467ecaChet Haase        @Override
249df32aa87150768795816852c6393306893467ecaChet Haase        public void onViewDetachedFromWindow(View v) {
250df32aa87150768795816852c6393306893467ecaChet Haase            removeListeners();
251df32aa87150768795816852c6393306893467ecaChet Haase
252df32aa87150768795816852c6393306893467ecaChet Haase            sPendingTransitions.remove(mSceneRoot);
253df32aa87150768795816852c6393306893467ecaChet Haase            ArrayList<Transition> runningTransitions = getRunningTransitions().get(mSceneRoot);
254df32aa87150768795816852c6393306893467ecaChet Haase            if (runningTransitions != null && runningTransitions.size() > 0) {
255df32aa87150768795816852c6393306893467ecaChet Haase                for (Transition runningTransition : runningTransitions) {
256df32aa87150768795816852c6393306893467ecaChet Haase                    runningTransition.resume();
257faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase                }
258df32aa87150768795816852c6393306893467ecaChet Haase            }
259df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.clearValues(true);
260faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        }
261df32aa87150768795816852c6393306893467ecaChet Haase
262df32aa87150768795816852c6393306893467ecaChet Haase        @Override
263df32aa87150768795816852c6393306893467ecaChet Haase        public boolean onPreDraw() {
264df32aa87150768795816852c6393306893467ecaChet Haase            removeListeners();
265df32aa87150768795816852c6393306893467ecaChet Haase            sPendingTransitions.remove(mSceneRoot);
266df32aa87150768795816852c6393306893467ecaChet Haase            // Add to running list, handle end to remove it
267df32aa87150768795816852c6393306893467ecaChet Haase            final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions =
268df32aa87150768795816852c6393306893467ecaChet Haase                    getRunningTransitions();
269df32aa87150768795816852c6393306893467ecaChet Haase            ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot);
270df32aa87150768795816852c6393306893467ecaChet Haase            ArrayList<Transition> previousRunningTransitions = null;
271df32aa87150768795816852c6393306893467ecaChet Haase            if (currentTransitions == null) {
272df32aa87150768795816852c6393306893467ecaChet Haase                currentTransitions = new ArrayList<Transition>();
273df32aa87150768795816852c6393306893467ecaChet Haase                runningTransitions.put(mSceneRoot, currentTransitions);
274df32aa87150768795816852c6393306893467ecaChet Haase            } else if (currentTransitions.size() > 0) {
275df32aa87150768795816852c6393306893467ecaChet Haase                previousRunningTransitions = new ArrayList<Transition>(currentTransitions);
276df32aa87150768795816852c6393306893467ecaChet Haase            }
277df32aa87150768795816852c6393306893467ecaChet Haase            currentTransitions.add(mTransition);
278df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.addListener(new Transition.TransitionListenerAdapter() {
279df32aa87150768795816852c6393306893467ecaChet Haase                @Override
280df32aa87150768795816852c6393306893467ecaChet Haase                public void onTransitionEnd(Transition transition) {
281df32aa87150768795816852c6393306893467ecaChet Haase                    ArrayList<Transition> currentTransitions =
282df32aa87150768795816852c6393306893467ecaChet Haase                            runningTransitions.get(mSceneRoot);
283df32aa87150768795816852c6393306893467ecaChet Haase                    currentTransitions.remove(transition);
284df32aa87150768795816852c6393306893467ecaChet Haase                }
285df32aa87150768795816852c6393306893467ecaChet Haase            });
286df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.captureValues(mSceneRoot, false);
287df32aa87150768795816852c6393306893467ecaChet Haase            if (previousRunningTransitions != null) {
288df32aa87150768795816852c6393306893467ecaChet Haase                for (Transition runningTransition : previousRunningTransitions) {
289df32aa87150768795816852c6393306893467ecaChet Haase                    runningTransition.resume();
290df32aa87150768795816852c6393306893467ecaChet Haase                }
291df32aa87150768795816852c6393306893467ecaChet Haase            }
292df32aa87150768795816852c6393306893467ecaChet Haase            mTransition.playTransition(mSceneRoot);
293df32aa87150768795816852c6393306893467ecaChet Haase
294df32aa87150768795816852c6393306893467ecaChet Haase            return true;
295df32aa87150768795816852c6393306893467ecaChet Haase        }
296df32aa87150768795816852c6393306893467ecaChet Haase    };
297faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
2984f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) {
2994f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
300c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase        // Capture current values
301199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot);
3024f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
303199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        if (runningTransitions != null && runningTransitions.size() > 0) {
304199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            for (Transition runningTransition : runningTransitions) {
305199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase                runningTransition.pause();
306199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            }
3074f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        }
3084f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
309199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase        if (transition != null) {
310199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase            transition.captureValues(sceneRoot, true);
311c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase        }
312c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase
3134f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        // Notify previous scene that it is being exited
314d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        Scene previousScene = Scene.getCurrentScene(sceneRoot);
3154f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        if (previousScene != null) {
3164f5072327d00822a2bfaff56df46cea2981ac90dChet Haase            previousScene.exit();
3174f5072327d00822a2bfaff56df46cea2981ac90dChet Haase        }
3184f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    }
3194f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
320faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
321faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * Change to the given scene, using the
322faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * appropriate transition for this particular scene change
323faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * (as specified to the TransitionManager, or the default
324faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * if no such transition exists).
325faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
326faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The Scene to change to
327faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
328faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public void transitionTo(Scene scene) {
329faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        // Auto transition if there is no transition declared for the Scene, but there is
330faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        // a root or parent view
331faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        changeScene(scene, getTransition(scene));
332faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
333faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
334faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
335faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
336d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to simply change to the given scene using
337faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the default transition for TransitionManager.
338faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
339faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The Scene to change to
340faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
341faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public static void go(Scene scene) {
342faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        changeScene(scene, sDefaultTransition);
343faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
344faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
345faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
346d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to simply change to the given scene using
347faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * the given transition.
348faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
349faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * <p>Passing in <code>null</code> for the transition parameter will
350faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * result in the scene changing without any transition running, and is
351faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * equivalent to calling {@link Scene#exit()} on the scene root's
352d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * current scene, followed by {@link Scene#enter()} on the scene
353d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * specified by the <code>scene</code> parameter.</p>
354faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
355faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param scene The Scene to change to
356faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param transition The transition to use for this scene change. A
357faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * value of null causes the scene change to happen with no transition.
358faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
359faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    public static void go(Scene scene, Transition transition) {
360faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase        changeScene(scene, transition);
361faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
362faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase
363faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    /**
364d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to animate, using the default transition,
365d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * to a new scene defined by all changes within the given scene root between
366d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * calling this method and the next rendering frame.
367d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Equivalent to calling {@link #beginDelayedTransition(ViewGroup, Transition)}
368d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * with a value of <code>null</code> for the <code>transition</code> parameter.
369faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     *
370faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     * @param sceneRoot The root of the View hierarchy to run the transition on.
371faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase     */
372d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase    public static void beginDelayedTransition(final ViewGroup sceneRoot) {
373d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase        beginDelayedTransition(sceneRoot, null);
374faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase    }
3754f5072327d00822a2bfaff56df46cea2981ac90dChet Haase
3764f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    /**
377d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase     * Convenience method to animate to a new scene defined by all changes within
3784f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * the given scene root between calling this method and the next rendering frame.
3794f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * Calling this method causes TransitionManager to capture current values in the
3804f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * scene root and then post a request to run a transition on the next frame.
3814f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * At that time, the new values in the scene root will be captured and changes
3824f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * will be animated. There is no need to create a Scene; it is implied by
3834f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * changes which take place between calling this method and the next frame when
3844f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * the transition begins.
3854f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     *
3864f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * <p>Calling this method several times before the next frame (for example, if
3874f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * unrelated code also wants to make dynamic changes and run a transition on
3884f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * the same scene root), only the first call will trigger capturing values
3894f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * and exiting the current scene. Subsequent calls to the method with the
3904f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * same scene root during the same frame will be ignored.</p>
3914f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     *
3924f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * <p>Passing in <code>null</code> for the transition parameter will
3934f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * cause the TransitionManager to use its default transition.</p>
3944f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     *
3954f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * @param sceneRoot The root of the View hierarchy to run the transition on.
3964f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * @param transition The transition to use for this change. A
3974f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     * value of null causes the TransitionManager to use the default transition.
3984f5072327d00822a2bfaff56df46cea2981ac90dChet Haase     */
3994f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) {
40023c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase        if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) {
40123c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            if (Transition.DBG) {
40223c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase                Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " +
40323c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase                        sceneRoot + ", " + transition);
40423c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            }
40523c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            sPendingTransitions.add(sceneRoot);
40623c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            if (transition == null) {
40723c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase                transition = sDefaultTransition;
40823c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            }
40923c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            final Transition transitionClone = transition.clone();
41023c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            sceneChangeSetup(sceneRoot, transitionClone);
41123c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            Scene.setCurrentScene(sceneRoot, null);
41223c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase            sceneChangeRunTransition(sceneRoot, transitionClone);
41323c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase        }
4144f5072327d00822a2bfaff56df46cea2981ac90dChet Haase    }
415faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase}
416