blob: 14ecc15325818a9031d671a668bf64bac1ca16a2 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.transition;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.util.AttributeSet;
import android.util.Xml;
import android.view.InflateException;
import android.view.ViewGroup;
import android.view.animation.AnimationUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
/**
* This class inflates scenes and transitions from resource files.
*
* Information on XML resource descriptions for transitions can be found for
* {@link android.R.styleable#Transition}, {@link android.R.styleable#TransitionSet},
* {@link android.R.styleable#TransitionTarget}, {@link android.R.styleable#Fade},
* and {@link android.R.styleable#TransitionManager}.
*/
public class TransitionInflater {
private Context mContext;
private TransitionInflater(Context context) {
mContext = context;
}
/**
* Obtains the TransitionInflater from the given context.
*/
public static TransitionInflater from(Context context) {
return new TransitionInflater(context);
}
/**
* Loads a {@link Transition} object from a resource
*
* @param resource The resource id of the transition to load
* @return The loaded Transition object
* @throws android.content.res.Resources.NotFoundException when the
* transition cannot be loaded
*/
public Transition inflateTransition(int resource) {
XmlResourceParser parser = mContext.getResources().getXml(resource);
try {
return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
parser.close();
}
}
/**
* Loads a {@link TransitionManager} object from a resource
*
*
*
* @param resource The resource id of the transition manager to load
* @return The loaded TransitionManager object
* @throws android.content.res.Resources.NotFoundException when the
* transition manager cannot be loaded
*/
public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
XmlResourceParser parser = mContext.getResources().getXml(resource);
try {
return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
} catch (XmlPullParserException e) {
InflateException ex = new InflateException(e.getMessage());
ex.initCause(e);
throw ex;
} catch (IOException e) {
InflateException ex = new InflateException(
parser.getPositionDescription()
+ ": " + e.getMessage());
ex.initCause(e);
throw ex;
} finally {
parser.close();
}
}
//
// Transition loading
//
private Transition createTransitionFromXml(XmlPullParser parser,
AttributeSet attrs, TransitionSet transitionSet)
throws XmlPullParserException, IOException {
Transition transition = null;
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
boolean newTransition = false;
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if ("fade".equals(name)) {
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.Fade);
int fadingMode = a.getInt(com.android.internal.R.styleable.Fade_fadingMode,
Fade.IN | Fade.OUT);
transition = new Fade(fadingMode);
newTransition = true;
} else if ("changeBounds".equals(name)) {
transition = new ChangeBounds();
newTransition = true;
} else if ("slide".equals(name)) {
transition = createSlideTransition(attrs);
newTransition = true;
} else if ("explode".equals(name)) {
transition = new Explode();
newTransition = true;
} else if ("moveImage".equals(name)) {
transition = new MoveImage();
newTransition = true;
} else if ("autoTransition".equals(name)) {
transition = new AutoTransition();
newTransition = true;
} else if ("recolor".equals(name)) {
transition = new Recolor();
newTransition = true;
} else if ("transitionSet".equals(name)) {
transition = new TransitionSet();
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TransitionSet);
int ordering = a.getInt(
com.android.internal.R.styleable.TransitionSet_transitionOrdering,
TransitionSet.ORDERING_TOGETHER);
((TransitionSet) transition).setOrdering(ordering);
createTransitionFromXml(parser, attrs, ((TransitionSet) transition));
a.recycle();
newTransition = true;
} else if ("targets".equals(name)) {
if (parser.getDepth() - 1 > depth && transition != null) {
// We're inside the child tag - add targets to the child
getTargetIds(parser, attrs, transition);
} else if (parser.getDepth() - 1 == depth && transitionSet != null) {
// add targets to the set
getTargetIds(parser, attrs, transitionSet);
}
}
if (transition != null || "targets".equals(name)) {
if (newTransition) {
loadTransition(transition, attrs);
if (transitionSet != null) {
transitionSet.addTransition(transition);
}
}
} else {
throw new RuntimeException("Unknown scene name: " + parser.getName());
}
}
return transition;
}
private Slide createSlideTransition(AttributeSet attrs) {
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.Slide);
int edge = a.getInt(com.android.internal.R.styleable.Slide_slideEdge, Slide.BOTTOM);
Slide slide = new Slide(edge);
a.recycle();
return slide;
}
private void getTargetIds(XmlPullParser parser,
AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
ArrayList<Integer> targetIds = new ArrayList<Integer>();
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equals("target")) {
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TransitionTarget);
int id = a.getResourceId(
com.android.internal.R.styleable.TransitionTarget_targetId, -1);
if (id >= 0) {
targetIds.add(id);
}
} else {
throw new RuntimeException("Unknown scene name: " + parser.getName());
}
}
int numTargets = targetIds.size();
if (numTargets > 0) {
for (int i = 0; i < numTargets; ++i) {
transition.addTarget(targetIds.get(i));
}
}
}
private Transition loadTransition(Transition transition, AttributeSet attrs)
throws Resources.NotFoundException {
TypedArray a =
mContext.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Transition);
long duration = a.getInt(com.android.internal.R.styleable.Transition_duration, -1);
if (duration >= 0) {
transition.setDuration(duration);
}
long startDelay = a.getInt(com.android.internal.R.styleable.Transition_startDelay, -1);
if (startDelay > 0) {
transition.setStartDelay(startDelay);
}
final int resID =
a.getResourceId(com.android.internal.R.styleable.Animator_interpolator, 0);
if (resID > 0) {
transition.setInterpolator(AnimationUtils.loadInterpolator(mContext, resID));
}
a.recycle();
return transition;
}
//
// TransitionManager loading
//
private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
// Make sure we are on a start tag.
int type;
int depth = parser.getDepth();
TransitionManager transitionManager = null;
while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
&& type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
String name = parser.getName();
if (name.equals("transitionManager")) {
transitionManager = new TransitionManager();
} else if (name.equals("transition") && (transitionManager != null)) {
loadTransition(attrs, sceneRoot, transitionManager);
} else {
throw new RuntimeException("Unknown scene name: " + parser.getName());
}
}
return transitionManager;
}
private void loadTransition(AttributeSet attrs, ViewGroup sceneRoot,
TransitionManager transitionManager) throws Resources.NotFoundException {
TypedArray a = mContext.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TransitionManager);
int transitionId = a.getResourceId(
com.android.internal.R.styleable.TransitionManager_transition, -1);
int fromId = a.getResourceId(
com.android.internal.R.styleable.TransitionManager_fromScene, -1);
Scene fromScene = (fromId < 0) ? null: Scene.getSceneForLayout(sceneRoot, fromId, mContext);
int toId = a.getResourceId(
com.android.internal.R.styleable.TransitionManager_toScene, -1);
Scene toScene = (toId < 0) ? null : Scene.getSceneForLayout(sceneRoot, toId, mContext);
if (transitionId >= 0) {
Transition transition = inflateTransition(transitionId);
if (transition != null) {
if (toScene == null) {
throw new RuntimeException("No toScene for transition ID " + transitionId);
}
if (fromScene == null) {
transitionManager.setTransition(toScene, transition);
} else {
transitionManager.setTransition(fromScene, toScene, transition);
}
}
}
a.recycle();
}
}