• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.transition;
18 
19 import android.content.Context;
20 import android.content.res.Resources;
21 import android.content.res.TypedArray;
22 import android.content.res.XmlResourceParser;
23 import android.util.AttributeSet;
24 import android.util.Xml;
25 import android.view.InflateException;
26 import android.view.ViewGroup;
27 import android.view.animation.AnimationUtils;
28 import org.xmlpull.v1.XmlPullParser;
29 import org.xmlpull.v1.XmlPullParserException;
30 
31 import java.io.IOException;
32 import java.util.ArrayList;
33 
34 /**
35  * This class inflates scenes and transitions from resource files.
36  *
37  * Information on XML resource descriptions for transitions can be found for
38  * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
39  * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
40  * and {@link android.R.styleable#TransitionManager}.
41  */
42 public class TransitionInflater {
43 
44     private Context mContext;
45 
TransitionInflater(Context context)46     private TransitionInflater(Context context) {
47         mContext = context;
48     }
49 
50     /**
51      * Obtains the TransitionInflater from the given context.
52      */
from(Context context)53     public static TransitionInflater from(Context context) {
54         return new TransitionInflater(context);
55     }
56 
57     /**
58      * Loads a {@link Transition} object from a resource
59      *
60      * @param resource The resource id of the transition to load
61      * @return The loaded Transition object
62      * @throws android.content.res.Resources.NotFoundException when the
63      * transition cannot be loaded
64      */
inflateTransition(int resource)65     public Transition inflateTransition(int resource) {
66         XmlResourceParser parser =  mContext.getResources().getXml(resource);
67         try {
68             return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
69         } catch (XmlPullParserException e) {
70             InflateException ex = new InflateException(e.getMessage());
71             ex.initCause(e);
72             throw ex;
73         } catch (IOException e) {
74             InflateException ex = new InflateException(
75                     parser.getPositionDescription()
76                             + ": " + e.getMessage());
77             ex.initCause(e);
78             throw ex;
79         } finally {
80             parser.close();
81         }
82     }
83 
84     /**
85      * Loads a {@link TransitionManager} object from a resource
86      *
87      *
88      *
89      * @param resource The resource id of the transition manager to load
90      * @return The loaded TransitionManager object
91      * @throws android.content.res.Resources.NotFoundException when the
92      * transition manager cannot be loaded
93      */
inflateTransitionManager(int resource, ViewGroup sceneRoot)94     public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
95         XmlResourceParser parser =  mContext.getResources().getXml(resource);
96         try {
97             return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
98         } catch (XmlPullParserException e) {
99             InflateException ex = new InflateException(e.getMessage());
100             ex.initCause(e);
101             throw ex;
102         } catch (IOException e) {
103             InflateException ex = new InflateException(
104                     parser.getPositionDescription()
105                             + ": " + e.getMessage());
106             ex.initCause(e);
107             throw ex;
108         } finally {
109             parser.close();
110         }
111     }
112 
113     //
114     // Transition loading
115     //
116 
createTransitionFromXml(XmlPullParser parser, AttributeSet attrs, TransitionSet transitionSet)117     private Transition createTransitionFromXml(XmlPullParser parser,
118             AttributeSet attrs, TransitionSet transitionSet)
119             throws XmlPullParserException, IOException {
120 
121         Transition transition = null;
122 
123         // Make sure we are on a start tag.
124         int type;
125         int depth = parser.getDepth();
126 
127         while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
128                 && type != XmlPullParser.END_DOCUMENT) {
129 
130             boolean newTransition = false;
131 
132             if (type != XmlPullParser.START_TAG) {
133                 continue;
134             }
135 
136             String  name = parser.getName();
137             if ("fade".equals(name)) {
138                 TypedArray a = mContext.obtainStyledAttributes(attrs,
139                         com.android.internal.R.styleable.Fade);
140                 int fadingMode = a.getInt(com.android.internal.R.styleable.Fade_fadingMode,
141                         Fade.IN | Fade.OUT);
142                 transition = new Fade(fadingMode);
143                 newTransition = true;
144             } else if ("changeBounds".equals(name)) {
145                 transition = new ChangeBounds();
146                 newTransition = true;
147             } else if ("slide".equals(name)) {
148                 transition = new Slide();
149                 newTransition = true;
150             } else if ("autoTransition".equals(name)) {
151                 transition = new AutoTransition();
152                 newTransition = true;
153             } else if ("recolor".equals(name)) {
154                 transition = new Recolor();
155                 newTransition = true;
156             } else if ("transitionSet".equals(name)) {
157                 transition = new TransitionSet();
158                 TypedArray a = mContext.obtainStyledAttributes(attrs,
159                         com.android.internal.R.styleable.TransitionSet);
160                 int ordering = a.getInt(
161                         com.android.internal.R.styleable.TransitionSet_transitionOrdering,
162                         TransitionSet.ORDERING_TOGETHER);
163                 ((TransitionSet) transition).setOrdering(ordering);
164                 createTransitionFromXml(parser, attrs, ((TransitionSet) transition));
165                 a.recycle();
166                 newTransition = true;
167             } else if ("targets".equals(name)) {
168                 if (parser.getDepth() - 1 > depth && transition != null) {
169                     // We're inside the child tag - add targets to the child
170                     getTargetIds(parser, attrs, transition);
171                 } else if (parser.getDepth() - 1 == depth && transitionSet != null) {
172                     // add targets to the set
173                     getTargetIds(parser, attrs, transitionSet);
174                 }
175             }
176             if (transition != null || "targets".equals(name)) {
177                 if (newTransition) {
178                     loadTransition(transition, attrs);
179                     if (transitionSet != null) {
180                         transitionSet.addTransition(transition);
181                     }
182                 }
183             } else {
184                 throw new RuntimeException("Unknown scene name: " + parser.getName());
185             }
186         }
187 
188         return transition;
189     }
190 
getTargetIds(XmlPullParser parser, AttributeSet attrs, Transition transition)191     private void getTargetIds(XmlPullParser parser,
192             AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
193 
194         // Make sure we are on a start tag.
195         int type;
196         int depth = parser.getDepth();
197 
198         ArrayList<Integer> targetIds = new ArrayList<Integer>();
199         while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
200                 && type != XmlPullParser.END_DOCUMENT) {
201 
202             if (type != XmlPullParser.START_TAG) {
203                 continue;
204             }
205 
206             String  name = parser.getName();
207             if (name.equals("target")) {
208                 TypedArray a = mContext.obtainStyledAttributes(attrs,
209                         com.android.internal.R.styleable.TransitionTarget);
210                 int id = a.getResourceId(
211                         com.android.internal.R.styleable.TransitionTarget_targetId, -1);
212                 if (id >= 0) {
213                     targetIds.add(id);
214                 }
215             } else {
216                 throw new RuntimeException("Unknown scene name: " + parser.getName());
217             }
218         }
219         int numTargets = targetIds.size();
220         if (numTargets > 0) {
221             for (int i = 0; i < numTargets; ++i) {
222                 transition.addTarget(targetIds.get(i));
223             }
224         }
225     }
226 
loadTransition(Transition transition, AttributeSet attrs)227     private Transition loadTransition(Transition transition, AttributeSet attrs)
228             throws Resources.NotFoundException {
229 
230         TypedArray a =
231                 mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition);
232         long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1);
233         if (duration >= 0) {
234             transition.setDuration(duration);
235         }
236         long startDelay = a.getInt(com.android.internal.R.styleable.Transition_startDelay, -1);
237         if (startDelay > 0) {
238             transition.setStartDelay(startDelay);
239         }
240         final int resID =
241                 a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
242         if (resID > 0) {
243             transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID));
244         }
245         a.recycle();
246         return transition;
247     }
248 
249     //
250     // TransitionManager loading
251     //
252 
createTransitionManagerFromXml(XmlPullParser parser, AttributeSet attrs, ViewGroup sceneRoot)253     private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
254             AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
255 
256         // Make sure we are on a start tag.
257         int type;
258         int depth = parser.getDepth();
259         TransitionManager transitionManager = null;
260 
261         while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
262                 && type != XmlPullParser.END_DOCUMENT) {
263 
264             if (type != XmlPullParser.START_TAG) {
265                 continue;
266             }
267 
268             String  name = parser.getName();
269             if (name.equals("transitionManager")) {
270                 transitionManager = new TransitionManager();
271             } else if (name.equals("transition") && (transitionManager != null)) {
272                 loadTransition(attrs, sceneRoot, transitionManager);
273             } else {
274                 throw new RuntimeException("Unknown scene name: " + parser.getName());
275             }
276         }
277         return transitionManager;
278     }
279 
loadTransition(AttributeSet attrs, ViewGroup sceneRoot, TransitionManager transitionManager)280     private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
281             TransitionManager transitionManager) throws Resources.NotFoundException {
282 
283         TypedArray a = mContext.obtainStyledAttributes(attrs,
284                 com.android.internal.R.styleable.TransitionManager);
285         int transitionId = a.getResourceId(
286                 com.android.internal.R.styleable.TransitionManager_transition, -1);
287         Scene fromScene = null, toScene = null;
288         int fromId = a.getResourceId(
289                 com.android.internal.R.styleable.TransitionManager_fromScene, -1);
290         if (fromId >= 0) fromScene = Scene.getSceneForLayout(sceneRoot, fromId, mContext);
291         int toId = a.getResourceId(
292                 com.android.internal.R.styleable.TransitionManager_toScene, -1);
293         if (toId >= 0) toScene = Scene.getSceneForLayout(sceneRoot, toId, mContext);
294         if (transitionId >= 0) {
295             Transition transition = inflateTransition(transitionId);
296             if (transition != null) {
297                 if (fromScene != null) {
298                     if (toScene == null){
299                         throw new RuntimeException("No matching toScene for given fromScene " +
300                                 "for transition ID " + transitionId);
301                     } else {
302                         transitionManager.setTransition(fromScene, toScene, transition);
303                     }
304                 } else if (toId >= 0) {
305                     transitionManager.setTransition(toScene, transition);
306                 }
307             }
308         }
309         a.recycle();
310     }
311 }
312