blob: 9f77d5ee7f9f6d8c421b32d01b60b0bcbe16d0f1 [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;
23import android.util.AttributeSet;
Chet Haasefaebd8f2012-05-18 14:17:57 -070024import android.util.Xml;
25import android.view.InflateException;
26import android.view.ViewGroup;
27import android.view.animation.AnimationUtils;
28import org.xmlpull.v1.XmlPullParser;
29import org.xmlpull.v1.XmlPullParserException;
30
31import java.io.IOException;
32import java.util.ArrayList;
Chet Haasefaebd8f2012-05-18 14:17:57 -070033
34/**
35 * This class inflates scenes and transitions from resource files.
Chet Haased82c8ac2013-08-26 14:20:16 -070036 *
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}.
Chet Haasefaebd8f2012-05-18 14:17:57 -070041 */
42public class TransitionInflater {
43
Chet Haasefaebd8f2012-05-18 14:17:57 -070044 private Context mContext;
Chet Haasefaebd8f2012-05-18 14:17:57 -070045
46 private TransitionInflater(Context context) {
47 mContext = context;
48 }
49
50 /**
51 * Obtains the TransitionInflater from the given context.
52 */
53 public static TransitionInflater from(Context context) {
Chet Haase58ad1222013-10-30 06:37:29 -070054 return new TransitionInflater(context);
Chet Haasefaebd8f2012-05-18 14:17:57 -070055 }
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 */
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 */
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
Chet Haasefaebd8f2012-05-18 14:17:57 -0700113 //
114 // Transition loading
115 //
116
117 private Transition createTransitionFromXml(XmlPullParser parser,
Chet Haased82c8ac2013-08-26 14:20:16 -0700118 AttributeSet attrs, TransitionSet transitionSet)
Chet Haasefaebd8f2012-05-18 14:17:57 -0700119 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)) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700138 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);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700143 newTransition = true;
Chet Haased82c8ac2013-08-26 14:20:16 -0700144 } else if ("changeBounds".equals(name)) {
145 transition = new ChangeBounds();
Chet Haasefaebd8f2012-05-18 14:17:57 -0700146 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;
Chet Haaseaafc91c2013-10-06 12:10:12 -0700156 } else if ("transitionSet".equals(name)) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700157 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();
Chet Haasefaebd8f2012-05-18 14:17:57 -0700166 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
Chet Haased82c8ac2013-08-26 14:20:16 -0700170 getTargetIds(parser, attrs, transition);
171 } else if (parser.getDepth() - 1 == depth && transitionSet != null) {
172 // add targets to the set
173 getTargetIds(parser, attrs, transitionSet);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700174 }
175 }
176 if (transition != null || "targets".equals(name)) {
177 if (newTransition) {
178 loadTransition(transition, attrs);
Chet Haased82c8ac2013-08-26 14:20:16 -0700179 if (transitionSet != null) {
180 transitionSet.addTransition(transition);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700181 }
182 }
183 } else {
184 throw new RuntimeException("Unknown scene name: " + parser.getName());
185 }
186 }
187
188 return transition;
189 }
190
Chet Haased82c8ac2013-08-26 14:20:16 -0700191 private void getTargetIds(XmlPullParser parser,
Chet Haasefaebd8f2012-05-18 14:17:57 -0700192 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,
Chet Haased82c8ac2013-08-26 14:20:16 -0700209 com.android.internal.R.styleable.TransitionTarget);
210 int id = a.getResourceId(
211 com.android.internal.R.styleable.TransitionTarget_targetId, -1);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700212 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) {
Chet Haased82c8ac2013-08-26 14:20:16 -0700221 for (int i = 0; i < numTargets; ++i) {
Chet Haaseff58f922013-09-11 13:08:18 -0700222 transition.addTarget(targetIds.get(i));
Chet Haasefaebd8f2012-05-18 14:17:57 -0700223 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700224 }
225 }
226
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 }
Chet Haased82c8ac2013-08-26 14:20:16 -0700236 long startDelay = a.getInt(com.android.internal.R.styleable.Transition_startDelay, -1);
237 if (startDelay > 0) {
238 transition.setStartDelay(startDelay);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700239 }
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
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
280 private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
Chet Haased82c8ac2013-08-26 14:20:16 -0700281 TransitionManager transitionManager) throws Resources.NotFoundException {
Chet Haasefaebd8f2012-05-18 14:17:57 -0700282
283 TypedArray a = mContext.obtainStyledAttributes(attrs,
284 com.android.internal.R.styleable.TransitionManager);
Chet Haased82c8ac2013-08-26 14:20:16 -0700285 int transitionId = a.getResourceId(
Chet Haasefaebd8f2012-05-18 14:17:57 -0700286 com.android.internal.R.styleable.TransitionManager_transition, -1);
287 Scene fromScene = null, toScene = null;
Chet Haased82c8ac2013-08-26 14:20:16 -0700288 int fromId = a.getResourceId(
Chet Haasefaebd8f2012-05-18 14:17:57 -0700289 com.android.internal.R.styleable.TransitionManager_fromScene, -1);
Chet Haased82c8ac2013-08-26 14:20:16 -0700290 if (fromId >= 0) fromScene = Scene.getSceneForLayout(sceneRoot, fromId, mContext);
291 int toId = a.getResourceId(
Chet Haasefaebd8f2012-05-18 14:17:57 -0700292 com.android.internal.R.styleable.TransitionManager_toScene, -1);
Chet Haased82c8ac2013-08-26 14:20:16 -0700293 if (toId >= 0) toScene = Scene.getSceneForLayout(sceneRoot, toId, mContext);
Chet Haasefaebd8f2012-05-18 14:17:57 -0700294 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 }
Chet Haasefaebd8f2012-05-18 14:17:57 -0700311}