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(); 18496e54dcfa4e2f5295b0441971f10906eeaa35328George Mount if (!sPendingTransitions.contains(sceneRoot)) { 18596e54dcfa4e2f5295b0441971f10906eeaa35328George Mount sPendingTransitions.add(sceneRoot); 186faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 18796e54dcfa4e2f5295b0441971f10906eeaa35328George Mount Transition transitionClone = null; 18896e54dcfa4e2f5295b0441971f10906eeaa35328George Mount if (transition != null) { 18996e54dcfa4e2f5295b0441971f10906eeaa35328George Mount transitionClone = transition.clone(); 19096e54dcfa4e2f5295b0441971f10906eeaa35328George Mount transitionClone.setSceneRoot(sceneRoot); 19196e54dcfa4e2f5295b0441971f10906eeaa35328George Mount } 1926ebe3de331efd00ba23bc4191d4a82cfa4c39160Chet Haase 19396e54dcfa4e2f5295b0441971f10906eeaa35328George Mount Scene oldScene = Scene.getCurrentScene(sceneRoot); 19496e54dcfa4e2f5295b0441971f10906eeaa35328George Mount if (oldScene != null && transitionClone != null && 19596e54dcfa4e2f5295b0441971f10906eeaa35328George Mount oldScene.isCreatedFromLayoutResource()) { 19696e54dcfa4e2f5295b0441971f10906eeaa35328George Mount transitionClone.setCanRemoveViews(true); 19796e54dcfa4e2f5295b0441971f10906eeaa35328George Mount } 198b7a7fc9d233bad507ce893882352618b13647058Chet Haase 19996e54dcfa4e2f5295b0441971f10906eeaa35328George Mount sceneChangeSetup(sceneRoot, transitionClone); 200faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 20196e54dcfa4e2f5295b0441971f10906eeaa35328George Mount scene.enter(); 202faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 20396e54dcfa4e2f5295b0441971f10906eeaa35328George Mount sceneChangeRunTransition(sceneRoot, transitionClone); 20496e54dcfa4e2f5295b0441971f10906eeaa35328George Mount } 2054f5072327d00822a2bfaff56df46cea2981ac90dChet Haase } 2064f5072327d00822a2bfaff56df46cea2981ac90dChet Haase 207199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase private static ArrayMap<ViewGroup, ArrayList<Transition>> getRunningTransitions() { 2087660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>> runningTransitions = 209199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase sRunningTransitions.get(); 2107660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase if (runningTransitions == null || runningTransitions.get() == null) { 2117660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase ArrayMap<ViewGroup, ArrayList<Transition>> transitions = 2127660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase new ArrayMap<ViewGroup, ArrayList<Transition>>(); 2137660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase runningTransitions = new WeakReference<ArrayMap<ViewGroup, ArrayList<Transition>>>( 2147660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase transitions); 215199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase sRunningTransitions.set(runningTransitions); 216199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 2177660d121b2ef21164ed33e6091e5dd50f5d0f939Chet Haase return runningTransitions.get(); 218199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 219199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 2204f5072327d00822a2bfaff56df46cea2981ac90dChet Haase private static void sceneChangeRunTransition(final ViewGroup sceneRoot, 2214f5072327d00822a2bfaff56df46cea2981ac90dChet Haase final Transition transition) { 222df32aa87150768795816852c6393306893467ecaChet Haase if (transition != null && sceneRoot != null) { 223df32aa87150768795816852c6393306893467ecaChet Haase MultiListener listener = new MultiListener(transition, sceneRoot); 224df32aa87150768795816852c6393306893467ecaChet Haase sceneRoot.addOnAttachStateChangeListener(listener); 225df32aa87150768795816852c6393306893467ecaChet Haase sceneRoot.getViewTreeObserver().addOnPreDrawListener(listener); 226df32aa87150768795816852c6393306893467ecaChet Haase } 227df32aa87150768795816852c6393306893467ecaChet Haase } 228df32aa87150768795816852c6393306893467ecaChet Haase 229df32aa87150768795816852c6393306893467ecaChet Haase /** 230df32aa87150768795816852c6393306893467ecaChet Haase * This private utility class is used to listen for both OnPreDraw and 231df32aa87150768795816852c6393306893467ecaChet Haase * OnAttachStateChange events. OnPreDraw events are the main ones we care 232df32aa87150768795816852c6393306893467ecaChet Haase * about since that's what triggers the transition to take place. 233df32aa87150768795816852c6393306893467ecaChet Haase * OnAttachStateChange events are also important in case the view is removed 234df32aa87150768795816852c6393306893467ecaChet Haase * from the hierarchy before the OnPreDraw event takes place; it's used to 235df32aa87150768795816852c6393306893467ecaChet Haase * clean up things since the OnPreDraw listener didn't get called in time. 236df32aa87150768795816852c6393306893467ecaChet Haase */ 237df32aa87150768795816852c6393306893467ecaChet Haase private static class MultiListener implements ViewTreeObserver.OnPreDrawListener, 238df32aa87150768795816852c6393306893467ecaChet Haase View.OnAttachStateChangeListener { 239df32aa87150768795816852c6393306893467ecaChet Haase 240df32aa87150768795816852c6393306893467ecaChet Haase Transition mTransition; 241df32aa87150768795816852c6393306893467ecaChet Haase ViewGroup mSceneRoot; 242df32aa87150768795816852c6393306893467ecaChet Haase 243df32aa87150768795816852c6393306893467ecaChet Haase MultiListener(Transition transition, ViewGroup sceneRoot) { 244df32aa87150768795816852c6393306893467ecaChet Haase mTransition = transition; 245df32aa87150768795816852c6393306893467ecaChet Haase mSceneRoot = sceneRoot; 246df32aa87150768795816852c6393306893467ecaChet Haase } 247df32aa87150768795816852c6393306893467ecaChet Haase 248df32aa87150768795816852c6393306893467ecaChet Haase private void removeListeners() { 249df32aa87150768795816852c6393306893467ecaChet Haase mSceneRoot.getViewTreeObserver().removeOnPreDrawListener(this); 250df32aa87150768795816852c6393306893467ecaChet Haase mSceneRoot.removeOnAttachStateChangeListener(this); 251df32aa87150768795816852c6393306893467ecaChet Haase } 252199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase 253df32aa87150768795816852c6393306893467ecaChet Haase @Override 254df32aa87150768795816852c6393306893467ecaChet Haase public void onViewAttachedToWindow(View v) { 255df32aa87150768795816852c6393306893467ecaChet Haase } 256df32aa87150768795816852c6393306893467ecaChet Haase 257df32aa87150768795816852c6393306893467ecaChet Haase @Override 258df32aa87150768795816852c6393306893467ecaChet Haase public void onViewDetachedFromWindow(View v) { 259df32aa87150768795816852c6393306893467ecaChet Haase removeListeners(); 260df32aa87150768795816852c6393306893467ecaChet Haase 261df32aa87150768795816852c6393306893467ecaChet Haase sPendingTransitions.remove(mSceneRoot); 262df32aa87150768795816852c6393306893467ecaChet Haase ArrayList<Transition> runningTransitions = getRunningTransitions().get(mSceneRoot); 263df32aa87150768795816852c6393306893467ecaChet Haase if (runningTransitions != null && runningTransitions.size() > 0) { 264df32aa87150768795816852c6393306893467ecaChet Haase for (Transition runningTransition : runningTransitions) { 265cf68aad3164303df59b2a669d186a94533c9c743George Mount runningTransition.resume(mSceneRoot); 266faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 267df32aa87150768795816852c6393306893467ecaChet Haase } 268df32aa87150768795816852c6393306893467ecaChet Haase mTransition.clearValues(true); 269faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 270df32aa87150768795816852c6393306893467ecaChet Haase 271df32aa87150768795816852c6393306893467ecaChet Haase @Override 272df32aa87150768795816852c6393306893467ecaChet Haase public boolean onPreDraw() { 273df32aa87150768795816852c6393306893467ecaChet Haase removeListeners(); 274e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette 275e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette // Don't start the transition if it's no longer pending. 276e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette if (!sPendingTransitions.remove(mSceneRoot)) { 277e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette return true; 278e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette } 279e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette 280df32aa87150768795816852c6393306893467ecaChet Haase // Add to running list, handle end to remove it 281df32aa87150768795816852c6393306893467ecaChet Haase final ArrayMap<ViewGroup, ArrayList<Transition>> runningTransitions = 282df32aa87150768795816852c6393306893467ecaChet Haase getRunningTransitions(); 283df32aa87150768795816852c6393306893467ecaChet Haase ArrayList<Transition> currentTransitions = runningTransitions.get(mSceneRoot); 284df32aa87150768795816852c6393306893467ecaChet Haase ArrayList<Transition> previousRunningTransitions = null; 285df32aa87150768795816852c6393306893467ecaChet Haase if (currentTransitions == null) { 286df32aa87150768795816852c6393306893467ecaChet Haase currentTransitions = new ArrayList<Transition>(); 287df32aa87150768795816852c6393306893467ecaChet Haase runningTransitions.put(mSceneRoot, currentTransitions); 288df32aa87150768795816852c6393306893467ecaChet Haase } else if (currentTransitions.size() > 0) { 289df32aa87150768795816852c6393306893467ecaChet Haase previousRunningTransitions = new ArrayList<Transition>(currentTransitions); 290df32aa87150768795816852c6393306893467ecaChet Haase } 291df32aa87150768795816852c6393306893467ecaChet Haase currentTransitions.add(mTransition); 292df32aa87150768795816852c6393306893467ecaChet Haase mTransition.addListener(new Transition.TransitionListenerAdapter() { 293df32aa87150768795816852c6393306893467ecaChet Haase @Override 294df32aa87150768795816852c6393306893467ecaChet Haase public void onTransitionEnd(Transition transition) { 295df32aa87150768795816852c6393306893467ecaChet Haase ArrayList<Transition> currentTransitions = 296df32aa87150768795816852c6393306893467ecaChet Haase runningTransitions.get(mSceneRoot); 297df32aa87150768795816852c6393306893467ecaChet Haase currentTransitions.remove(transition); 298df32aa87150768795816852c6393306893467ecaChet Haase } 299df32aa87150768795816852c6393306893467ecaChet Haase }); 300df32aa87150768795816852c6393306893467ecaChet Haase mTransition.captureValues(mSceneRoot, false); 301df32aa87150768795816852c6393306893467ecaChet Haase if (previousRunningTransitions != null) { 302df32aa87150768795816852c6393306893467ecaChet Haase for (Transition runningTransition : previousRunningTransitions) { 303cf68aad3164303df59b2a669d186a94533c9c743George Mount runningTransition.resume(mSceneRoot); 304df32aa87150768795816852c6393306893467ecaChet Haase } 305df32aa87150768795816852c6393306893467ecaChet Haase } 306df32aa87150768795816852c6393306893467ecaChet Haase mTransition.playTransition(mSceneRoot); 307df32aa87150768795816852c6393306893467ecaChet Haase 308df32aa87150768795816852c6393306893467ecaChet Haase return true; 309df32aa87150768795816852c6393306893467ecaChet Haase } 310df32aa87150768795816852c6393306893467ecaChet Haase }; 311faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 3124f5072327d00822a2bfaff56df46cea2981ac90dChet Haase private static void sceneChangeSetup(ViewGroup sceneRoot, Transition transition) { 3134f5072327d00822a2bfaff56df46cea2981ac90dChet Haase 314c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase // Capture current values 315199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot); 3164f5072327d00822a2bfaff56df46cea2981ac90dChet Haase 317199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase if (runningTransitions != null && runningTransitions.size() > 0) { 318199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase for (Transition runningTransition : runningTransitions) { 319cf68aad3164303df59b2a669d186a94533c9c743George Mount runningTransition.pause(sceneRoot); 320199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase } 3214f5072327d00822a2bfaff56df46cea2981ac90dChet Haase } 3224f5072327d00822a2bfaff56df46cea2981ac90dChet Haase 323199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase if (transition != null) { 324199acdfcc98e852975dd7edfbcb822ba5e73146fChet Haase transition.captureValues(sceneRoot, true); 325c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase } 326c81a8493884c7f432d6bd5b98aca3fbdc93b355bChet Haase 3274f5072327d00822a2bfaff56df46cea2981ac90dChet Haase // Notify previous scene that it is being exited 328d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase Scene previousScene = Scene.getCurrentScene(sceneRoot); 3294f5072327d00822a2bfaff56df46cea2981ac90dChet Haase if (previousScene != null) { 3304f5072327d00822a2bfaff56df46cea2981ac90dChet Haase previousScene.exit(); 3314f5072327d00822a2bfaff56df46cea2981ac90dChet Haase } 3324f5072327d00822a2bfaff56df46cea2981ac90dChet Haase } 3334f5072327d00822a2bfaff56df46cea2981ac90dChet Haase 334faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase /** 335faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * Change to the given scene, using the 336faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * appropriate transition for this particular scene change 337faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * (as specified to the TransitionManager, or the default 338faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * if no such transition exists). 339faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 340faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * @param scene The Scene to change to 341faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */ 342faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase public void transitionTo(Scene scene) { 343faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase // Auto transition if there is no transition declared for the Scene, but there is 344faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase // a root or parent view 345faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase changeScene(scene, getTransition(scene)); 346faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 347faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 348faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase /** 349d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * Convenience method to simply change to the given scene using 350faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * the default transition for TransitionManager. 351faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 352faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * @param scene The Scene to change to 353faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */ 354faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase public static void go(Scene scene) { 355faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase changeScene(scene, sDefaultTransition); 356faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 357faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 358faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase /** 359d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * Convenience method to simply change to the given scene using 360faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * the given transition. 361faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 362faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * <p>Passing in <code>null</code> for the transition parameter will 363faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * result in the scene changing without any transition running, and is 364faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * equivalent to calling {@link Scene#exit()} on the scene root's 365d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * current scene, followed by {@link Scene#enter()} on the scene 366d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * specified by the <code>scene</code> parameter.</p> 367faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 368faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * @param scene The Scene to change to 369faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * @param transition The transition to use for this scene change. A 370faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * value of null causes the scene change to happen with no transition. 371faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */ 372faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase public static void go(Scene scene, Transition transition) { 373faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase changeScene(scene, transition); 374faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 375faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase 376faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase /** 377d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * Convenience method to animate, using the default transition, 378d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * to a new scene defined by all changes within the given scene root between 379d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * calling this method and the next rendering frame. 380d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * Equivalent to calling {@link #beginDelayedTransition(ViewGroup, Transition)} 381d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * with a value of <code>null</code> for the <code>transition</code> parameter. 382faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * 383faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase * @param sceneRoot The root of the View hierarchy to run the transition on. 384faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase */ 385d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase public static void beginDelayedTransition(final ViewGroup sceneRoot) { 386d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase beginDelayedTransition(sceneRoot, null); 387faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase } 3884f5072327d00822a2bfaff56df46cea2981ac90dChet Haase 3894f5072327d00822a2bfaff56df46cea2981ac90dChet Haase /** 390d82c8ac4db7091d2e976af4c89a1734465d20cd2Chet Haase * Convenience method to animate to a new scene defined by all changes within 3914f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * the given scene root between calling this method and the next rendering frame. 3924f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * Calling this method causes TransitionManager to capture current values in the 3934f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * scene root and then post a request to run a transition on the next frame. 3944f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * At that time, the new values in the scene root will be captured and changes 3954f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * will be animated. There is no need to create a Scene; it is implied by 3964f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * changes which take place between calling this method and the next frame when 3974f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * the transition begins. 3984f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * 3994f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * <p>Calling this method several times before the next frame (for example, if 4004f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * unrelated code also wants to make dynamic changes and run a transition on 4014f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * the same scene root), only the first call will trigger capturing values 4024f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * and exiting the current scene. Subsequent calls to the method with the 4034f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * same scene root during the same frame will be ignored.</p> 4044f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * 4054f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * <p>Passing in <code>null</code> for the transition parameter will 4064f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * cause the TransitionManager to use its default transition.</p> 4074f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * 4084f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * @param sceneRoot The root of the View hierarchy to run the transition on. 4094f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * @param transition The transition to use for this change. A 4104f5072327d00822a2bfaff56df46cea2981ac90dChet Haase * value of null causes the TransitionManager to use the default transition. 4114f5072327d00822a2bfaff56df46cea2981ac90dChet Haase */ 4124f5072327d00822a2bfaff56df46cea2981ac90dChet Haase public static void beginDelayedTransition(final ViewGroup sceneRoot, Transition transition) { 41323c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase if (!sPendingTransitions.contains(sceneRoot) && sceneRoot.isLaidOut()) { 41423c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase if (Transition.DBG) { 41523c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase Log.d(LOG_TAG, "beginDelayedTransition: root, transition = " + 41623c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase sceneRoot + ", " + transition); 41723c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase } 41823c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase sPendingTransitions.add(sceneRoot); 41923c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase if (transition == null) { 42023c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase transition = sDefaultTransition; 42123c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase } 42223c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase final Transition transitionClone = transition.clone(); 42323c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase sceneChangeSetup(sceneRoot, transitionClone); 42423c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase Scene.setCurrentScene(sceneRoot, null); 42523c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase sceneChangeRunTransition(sceneRoot, transitionClone); 42623c61f6bc57a611d97d333bce0d8fe00ab81af4cChet Haase } 4274f5072327d00822a2bfaff56df46cea2981ac90dChet Haase } 428e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette 429e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette /** 430e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette * Ends all pending and ongoing transitions on the specified scene root. 431e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette * 432e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette * @param sceneRoot The root of the View hierarchy to end transitions on. 433e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette */ 434e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette public static void endTransitions(final ViewGroup sceneRoot) { 435e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette sPendingTransitions.remove(sceneRoot); 436e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette 437e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette final ArrayList<Transition> runningTransitions = getRunningTransitions().get(sceneRoot); 438800320933e849cc370a96d032c65fbddcc4fac9dGeorge Mount if (runningTransitions != null && !runningTransitions.isEmpty()) { 439800320933e849cc370a96d032c65fbddcc4fac9dGeorge Mount // Make a copy in case this is called by an onTransitionEnd listener 440800320933e849cc370a96d032c65fbddcc4fac9dGeorge Mount ArrayList<Transition> copy = new ArrayList(runningTransitions); 441800320933e849cc370a96d032c65fbddcc4fac9dGeorge Mount for (int i = copy.size() - 1; i >= 0; i--) { 442800320933e849cc370a96d032c65fbddcc4fac9dGeorge Mount final Transition transition = copy.get(i); 443413739e8c62a7cd83ec519203a2628504b1b0d16George Mount transition.forceToEnd(sceneRoot); 444e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette } 445e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette } 446e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette 447e025ed2f26858dae5f40a94a2e1ac0db808a6950Alan Viverette } 448faebd8f0795b7d275fb4e503533c8c0c4a9acc21Chet Haase} 449