blob: 9fa554c3b3a63f792e9db60886de1a1a3e6b4a02 [file] [log] [blame]
Chet Haasefaebd8f2012-05-18 14:17:57 -07001/*
2 * Copyright (C) 2013 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Chet Haase6ebe3de2013-06-17 16:50:50 -070016
Chet Haased82c8ac2013-08-26 14:20:16 -070017package android.transition;
Chet Haasefaebd8f2012-05-18 14:17:57 -070018
19import android.content.Context;
20import android.content.res.Resources;
21import android.content.res.TypedArray;
22import android.content.res.XmlResourceParser;
Adam Powellcfbe9be2013-11-06 14:58:58 -080023import android.text.TextUtils;
Chet Haasefaebd8f2012-05-18 14:17:57 -070024import android.util.AttributeSet;
Chet Haasefaebd8f2012-05-18 14:17:57 -070025import android.util.Xml;
26import android.view.InflateException;
27import android.view.ViewGroup;
28import android.view.animation.AnimationUtils;
29import org.xmlpull.v1.XmlPullParser;
30import org.xmlpull.v1.XmlPullParserException;
31
32import java.io.IOException;
33import java.util.ArrayList;
Chet Haasefaebd8f2012-05-18 14:17:57 -070034
35/**
36 * This class inflates scenes and transitions from resource files.
Chet Haased82c8ac2013-08-26 14:20:16 -070037 *
38 * Information on XML resource descriptions for transitions can be found for
39 * {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
40 * {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
41 * and {@link android.R.styleable#TransitionManager}.
Chet Haasefaebd8f2012-05-18 14:17:57 -070042 */
43public class TransitionInflater {
44
Chet Haasefaebd8f2012-05-18 14:17:57 -070045 private Context mContext;
Chet Haasefaebd8f2012-05-18 14:17:57 -070046
47 private TransitionInflater(Context context) {
48 mContext = context;
49 }
50
51 /**
52 * Obtains the TransitionInflater from the given context.
53 */
54 public static TransitionInflater from(Context context) {
Chet Haase58ad1222013-10-30 06:37:29 -070055 return new TransitionInflater(context);
Chet Haasefaebd8f2012-05-18 14:17:57 -070056 }
57
58 /**
59 * Loads a {@link Transition} object from a resource
60 *
61 * @param resource The resource id of the transition to load
62 * @return The loaded Transition object
63 * @throws android.content.res.Resources.NotFoundException when the
64 * transition cannot be loaded
65 */
66 public Transition inflateTransition(int resource) {
67 XmlResourceParser parser = mContext.getResources().getXml(resource);
68 try {
69 return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
70 } catch (XmlPullParserException e) {
71 InflateException ex = new InflateException(e.getMessage());
72 ex.initCause(e);
73 throw ex;
74 } catch (IOException e) {
75 InflateException ex = new InflateException(
76 parser.getPositionDescription()
77 + ": " + e.getMessage());
78 ex.initCause(e);
79 throw ex;
80 } finally {
81 parser.close();
82 }
83 }
84
85 /**
86 * Loads a {@link TransitionManager} object from a resource
87 *
88 *
89 *
90 * @param resource The resource id of the transition manager to load
91 * @return The loaded TransitionManager object
92 * @throws android.content.res.Resources.NotFoundException when the
93 * transition manager cannot be loaded
94 */
95 public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
96 XmlResourceParser parser = mContext.getResources().getXml(resource);
97 try {
98 return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
99 } catch (XmlPullParserException e) {
100 InflateException ex = new InflateException(e.getMessage());
101 ex.initCause(e);
102 throw ex;
103 } catch (IOException e) {
104 InflateException ex = new InflateException(
105 parser.getPositionDescription()
106 + ": " + e.getMessage());
107 ex.initCause(e);
108 throw ex;
109 } finally {
110 parser.close();
111 }
112 }
113
Chet Haasefaebd8f2012-05-18 14:17:57 -0700114 //
115 // Transition loading
116 //
117
118 private Transition createTransitionFromXml(XmlPullParser parser,
Chet Haased82c8ac2013-08-26 14:20:16 -0700119 AttributeSet attrs, TransitionSet transitionSet)
Chet Haasefaebd8f2012-05-18 14:17:57 -0700120 throws XmlPullParserException, IOException {
121
122 Transition transition = null;
123
124 // Make sure we are on a start tag.
125 int type;
126 int depth = parser.getDepth();
127
128 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
129 && type != XmlPullParser.END_DOCUMENT) {
130
131 boolean newTransition = false;
132
133 if (type != XmlPullParser.START_TAG) {
134 continue;
135 }
136
137 String name = parser.getName();
138 if ("fade".equals(name)) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700139 TypedArray a = mContext.obtainStyledAttributes(attrs,
140 com.android.internal.R.styleable.Fade);
141 int fadingMode = a.getInt(com.android.internal.R.styleable.Fade_fadingMode,
142 Fade.IN | Fade.OUT);
143 transition = new Fade(fadingMode);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700144 newTransition = true;
Chet Haased82c8ac2013-08-26 14:20:16 -0700145 } else if ("changeBounds".equals(name)) {
146 transition = new ChangeBounds();
Chet Haasefaebd8f2012-05-18 14:17:57 -0700147 newTransition = true;
148 } else if ("slide".equals(name)) {
149 transition = new Slide();
150 newTransition = true;
151 } else if ("autoTransition".equals(name)) {
152 transition = new AutoTransition();
153 newTransition = true;
154 } else if ("recolor".equals(name)) {
155 transition = new Recolor();
156 newTransition = true;
Chet Haaseaafc91c2013-10-06 12:10:12 -0700157 } else if ("transitionSet".equals(name)) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700158 transition = new TransitionSet();
159 TypedArray a = mContext.obtainStyledAttributes(attrs,
160 com.android.internal.R.styleable.TransitionSet);
161 int ordering = a.getInt(
162 com.android.internal.R.styleable.TransitionSet_transitionOrdering,
163 TransitionSet.ORDERING_TOGETHER);
164 ((TransitionSet) transition).setOrdering(ordering);
165 createTransitionFromXml(parser, attrs, ((TransitionSet) transition));
166 a.recycle();
Chet Haasefaebd8f2012-05-18 14:17:57 -0700167 newTransition = true;
168 } else if ("targets".equals(name)) {
169 if (parser.getDepth() - 1 > depth && transition != null) {
170 // We're inside the child tag - add targets to the child
Chet Haased82c8ac2013-08-26 14:20:16 -0700171 getTargetIds(parser, attrs, transition);
172 } else if (parser.getDepth() - 1 == depth && transitionSet != null) {
173 // add targets to the set
174 getTargetIds(parser, attrs, transitionSet);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700175 }
176 }
177 if (transition != null || "targets".equals(name)) {
178 if (newTransition) {
179 loadTransition(transition, attrs);
Chet Haased82c8ac2013-08-26 14:20:16 -0700180 if (transitionSet != null) {
181 transitionSet.addTransition(transition);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700182 }
183 }
184 } else {
185 throw new RuntimeException("Unknown scene name: " + parser.getName());
186 }
187 }
188
189 return transition;
190 }
191
Chet Haased82c8ac2013-08-26 14:20:16 -0700192 private void getTargetIds(XmlPullParser parser,
Chet Haasefaebd8f2012-05-18 14:17:57 -0700193 AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
194
195 // Make sure we are on a start tag.
196 int type;
197 int depth = parser.getDepth();
198
199 ArrayList<Integer> targetIds = new ArrayList<Integer>();
200 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
201 && type != XmlPullParser.END_DOCUMENT) {
202
203 if (type != XmlPullParser.START_TAG) {
204 continue;
205 }
206
207 String name = parser.getName();
208 if (name.equals("target")) {
209 TypedArray a = mContext.obtainStyledAttributes(attrs,
Chet Haased82c8ac2013-08-26 14:20:16 -0700210 com.android.internal.R.styleable.TransitionTarget);
211 int id = a.getResourceId(
212 com.android.internal.R.styleable.TransitionTarget_targetId, -1);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700213 if (id >= 0) {
214 targetIds.add(id);
215 }
216 } else {
217 throw new RuntimeException("Unknown scene name: " + parser.getName());
218 }
219 }
220 int numTargets = targetIds.size();
221 if (numTargets > 0) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700222 for (int i = 0; i < numTargets; ++i) {
Chet Haaseff58f922013-09-11 13:08:18 -0700223 transition.addTarget(targetIds.get(i));
Chet Haasefaebd8f2012-05-18 14:17:57 -0700224 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700225 }
226 }
227
228 private Transition loadTransition(Transition transition, AttributeSet attrs)
229 throws Resources.NotFoundException {
230
231 TypedArray a =
232 mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition);
233 long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1);
234 if (duration >= 0) {
235 transition.setDuration(duration);
236 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700237 long startDelay = a.getInt(com.android.internal.R.styleable.Transition_startDelay, -1);
238 if (startDelay > 0) {
239 transition.setStartDelay(startDelay);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700240 }
241 final int resID =
242 a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
243 if (resID > 0) {
244 transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID));
245 }
246 a.recycle();
247 return transition;
248 }
249
250 //
251 // TransitionManager loading
252 //
253
254 private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
255 AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
256
257 // Make sure we are on a start tag.
258 int type;
259 int depth = parser.getDepth();
260 TransitionManager transitionManager = null;
261
262 while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
263 && type != XmlPullParser.END_DOCUMENT) {
264
265 if (type != XmlPullParser.START_TAG) {
266 continue;
267 }
268
269 String name = parser.getName();
270 if (name.equals("transitionManager")) {
271 transitionManager = new TransitionManager();
272 } else if (name.equals("transition") && (transitionManager != null)) {
273 loadTransition(attrs, sceneRoot, transitionManager);
274 } else {
275 throw new RuntimeException("Unknown scene name: " + parser.getName());
276 }
277 }
278 return transitionManager;
279 }
280
281 private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
Chet Haased82c8ac2013-08-26 14:20:16 -0700282 TransitionManager transitionManager) throws Resources.NotFoundException {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700283
284 TypedArray a = mContext.obtainStyledAttributes(attrs,
285 com.android.internal.R.styleable.TransitionManager);
Chet Haased82c8ac2013-08-26 14:20:16 -0700286 int transitionId = a.getResourceId(
Chet Haasefaebd8f2012-05-18 14:17:57 -0700287 com.android.internal.R.styleable.TransitionManager_transition, -1);
288 Scene fromScene = null, toScene = null;
Chet Haased82c8ac2013-08-26 14:20:16 -0700289 int fromId = a.getResourceId(
Chet Haasefaebd8f2012-05-18 14:17:57 -0700290 com.android.internal.R.styleable.TransitionManager_fromScene, -1);
Chet Haased82c8ac2013-08-26 14:20:16 -0700291 if (fromId >= 0) fromScene = Scene.getSceneForLayout(sceneRoot, fromId, mContext);
292 int toId = a.getResourceId(
Chet Haasefaebd8f2012-05-18 14:17:57 -0700293 com.android.internal.R.styleable.TransitionManager_toScene, -1);
Chet Haased82c8ac2013-08-26 14:20:16 -0700294 if (toId >= 0) toScene = Scene.getSceneForLayout(sceneRoot, toId, mContext);
Adam Powellcfbe9be2013-11-06 14:58:58 -0800295 String fromName = a.getString(
296 com.android.internal.R.styleable.TransitionManager_fromSceneName);
297 String toName = a.getString(
298 com.android.internal.R.styleable.TransitionManager_toSceneName);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700299 if (transitionId >= 0) {
300 Transition transition = inflateTransition(transitionId);
301 if (transition != null) {
302 if (fromScene != null) {
Adam Powellcfbe9be2013-11-06 14:58:58 -0800303 boolean hasDest = false;
304 if (toScene != null) {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700305 transitionManager.setTransition(fromScene, toScene, transition);
Adam Powellcfbe9be2013-11-06 14:58:58 -0800306 hasDest = true;
307 }
308
309 if (!TextUtils.isEmpty(toName)) {
310 transitionManager.setTransition(fromScene, toName, transition);
311 hasDest = true;
312 }
313
314 if (!hasDest) {
315 throw new RuntimeException("No matching toScene or toSceneName for given " +
316 "fromScene for transition ID " + transitionId);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700317 }
318 } else if (toId >= 0) {
319 transitionManager.setTransition(toScene, transition);
320 }
Adam Powellcfbe9be2013-11-06 14:58:58 -0800321 if (fromName != null) {
322 if (toScene != null) {
323 transitionManager.setTransition(fromName, toScene, transition);
324 } else {
325 throw new RuntimeException("No matching toScene for given fromSceneName " +
326 "for transition ID " + transitionId);
327 }
328 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700329 }
330 }
331 a.recycle();
332 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700333}