347 lines
14 KiB
Java
347 lines
14 KiB
Java
![]() |
/*
|
||
|
* 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.annotation.TransitionRes;
|
||
|
import android.content.Context;
|
||
|
import android.content.res.Resources;
|
||
|
import android.content.res.TypedArray;
|
||
|
import android.content.res.XmlResourceParser;
|
||
|
import android.util.ArrayMap;
|
||
|
import android.util.AttributeSet;
|
||
|
import android.util.Xml;
|
||
|
import android.view.InflateException;
|
||
|
import android.view.ViewGroup;
|
||
|
|
||
|
import com.android.internal.R;
|
||
|
|
||
|
import org.xmlpull.v1.XmlPullParser;
|
||
|
import org.xmlpull.v1.XmlPullParserException;
|
||
|
|
||
|
import java.io.IOException;
|
||
|
import java.lang.reflect.Constructor;
|
||
|
import java.lang.reflect.InvocationTargetException;
|
||
|
|
||
|
/**
|
||
|
* 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 static final Class<?>[] sConstructorSignature = new Class[] {
|
||
|
Context.class, AttributeSet.class};
|
||
|
private final static ArrayMap<String, Constructor> sConstructors =
|
||
|
new ArrayMap<String, Constructor>();
|
||
|
|
||
|
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(@TransitionRes int resource) {
|
||
|
//noinspection ResourceType
|
||
|
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(@TransitionRes int resource,
|
||
|
ViewGroup sceneRoot) {
|
||
|
//noinspection ResourceType
|
||
|
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, Transition parent)
|
||
|
throws XmlPullParserException, IOException {
|
||
|
|
||
|
Transition transition = null;
|
||
|
|
||
|
// Make sure we are on a start tag.
|
||
|
int type;
|
||
|
int depth = parser.getDepth();
|
||
|
|
||
|
TransitionSet transitionSet = (parent instanceof TransitionSet)
|
||
|
? (TransitionSet) parent : 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 ("fade".equals(name)) {
|
||
|
transition = new Fade(mContext, attrs);
|
||
|
} else if ("changeBounds".equals(name)) {
|
||
|
transition = new ChangeBounds(mContext, attrs);
|
||
|
} else if ("slide".equals(name)) {
|
||
|
transition = new Slide(mContext, attrs);
|
||
|
} else if ("explode".equals(name)) {
|
||
|
transition = new Explode(mContext, attrs);
|
||
|
} else if ("changeImageTransform".equals(name)) {
|
||
|
transition = new ChangeImageTransform(mContext, attrs);
|
||
|
} else if ("changeTransform".equals(name)) {
|
||
|
transition = new ChangeTransform(mContext, attrs);
|
||
|
} else if ("changeClipBounds".equals(name)) {
|
||
|
transition = new ChangeClipBounds(mContext, attrs);
|
||
|
} else if ("autoTransition".equals(name)) {
|
||
|
transition = new AutoTransition(mContext, attrs);
|
||
|
} else if ("recolor".equals(name)) {
|
||
|
transition = new Recolor(mContext, attrs);
|
||
|
} else if ("changeScroll".equals(name)) {
|
||
|
transition = new ChangeScroll(mContext, attrs);
|
||
|
} else if ("transitionSet".equals(name)) {
|
||
|
transition = new TransitionSet(mContext, attrs);
|
||
|
} else if ("transition".equals(name)) {
|
||
|
transition = (Transition) createCustom(attrs, Transition.class, "transition");
|
||
|
} else if ("targets".equals(name)) {
|
||
|
getTargetIds(parser, attrs, parent);
|
||
|
} else if ("arcMotion".equals(name)) {
|
||
|
parent.setPathMotion(new ArcMotion(mContext, attrs));
|
||
|
} else if ("pathMotion".equals(name)) {
|
||
|
parent.setPathMotion((PathMotion)createCustom(attrs, PathMotion.class, "pathMotion"));
|
||
|
} else if ("patternPathMotion".equals(name)) {
|
||
|
parent.setPathMotion(new PatternPathMotion(mContext, attrs));
|
||
|
} else {
|
||
|
throw new RuntimeException("Unknown scene name: " + parser.getName());
|
||
|
}
|
||
|
if (transition != null) {
|
||
|
if (!parser.isEmptyElementTag()) {
|
||
|
createTransitionFromXml(parser, attrs, transition);
|
||
|
}
|
||
|
if (transitionSet != null) {
|
||
|
transitionSet.addTransition(transition);
|
||
|
transition = null;
|
||
|
} else if (parent != null) {
|
||
|
throw new InflateException("Could not add transition to another transition.");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return transition;
|
||
|
}
|
||
|
|
||
|
private Object createCustom(AttributeSet attrs, Class expectedType, String tag) {
|
||
|
String className = attrs.getAttributeValue(null, "class");
|
||
|
|
||
|
if (className == null) {
|
||
|
throw new InflateException(tag + " tag must have a 'class' attribute");
|
||
|
}
|
||
|
|
||
|
try {
|
||
|
synchronized (sConstructors) {
|
||
|
Constructor constructor = sConstructors.get(className);
|
||
|
if (constructor == null) {
|
||
|
Class c = mContext.getClassLoader().loadClass(className)
|
||
|
.asSubclass(expectedType);
|
||
|
if (c != null) {
|
||
|
constructor = c.getConstructor(sConstructorSignature);
|
||
|
constructor.setAccessible(true);
|
||
|
sConstructors.put(className, constructor);
|
||
|
}
|
||
|
}
|
||
|
return constructor.newInstance(mContext, attrs);
|
||
|
}
|
||
|
} catch (InstantiationException e) {
|
||
|
throw new InflateException("Could not instantiate " + expectedType + " class " +
|
||
|
className, e);
|
||
|
} catch (ClassNotFoundException e) {
|
||
|
throw new InflateException("Could not instantiate " + expectedType + " class " +
|
||
|
className, e);
|
||
|
} catch (InvocationTargetException e) {
|
||
|
throw new InflateException("Could not instantiate " + expectedType + " class " +
|
||
|
className, e);
|
||
|
} catch (NoSuchMethodException e) {
|
||
|
throw new InflateException("Could not instantiate " + expectedType + " class " +
|
||
|
className, e);
|
||
|
} catch (IllegalAccessException e) {
|
||
|
throw new InflateException("Could not instantiate " + expectedType + " class " +
|
||
|
className, e);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
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();
|
||
|
|
||
|
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, R.styleable.TransitionTarget);
|
||
|
int id = a.getResourceId(R.styleable.TransitionTarget_targetId, 0);
|
||
|
String transitionName;
|
||
|
if (id != 0) {
|
||
|
transition.addTarget(id);
|
||
|
} else if ((id = a.getResourceId(R.styleable.TransitionTarget_excludeId, 0)) != 0) {
|
||
|
transition.excludeTarget(id, true);
|
||
|
} else if ((transitionName = a.getString(R.styleable.TransitionTarget_targetName))
|
||
|
!= null) {
|
||
|
transition.addTarget(transitionName);
|
||
|
} else if ((transitionName = a.getString(R.styleable.TransitionTarget_excludeName))
|
||
|
!= null) {
|
||
|
transition.excludeTarget(transitionName, true);
|
||
|
} else {
|
||
|
String className = a.getString(R.styleable.TransitionTarget_excludeClass);
|
||
|
try {
|
||
|
if (className != null) {
|
||
|
Class clazz = Class.forName(className);
|
||
|
transition.excludeTarget(clazz, true);
|
||
|
} else if ((className =
|
||
|
a.getString(R.styleable.TransitionTarget_targetClass)) != null) {
|
||
|
Class clazz = Class.forName(className);
|
||
|
transition.addTarget(clazz);
|
||
|
}
|
||
|
} catch (ClassNotFoundException e) {
|
||
|
a.recycle();
|
||
|
throw new RuntimeException("Could not create " + className, e);
|
||
|
}
|
||
|
}
|
||
|
a.recycle();
|
||
|
} else {
|
||
|
throw new RuntimeException("Unknown scene name: " + parser.getName());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//
|
||
|
// 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, R.styleable.TransitionManager);
|
||
|
int transitionId = a.getResourceId(R.styleable.TransitionManager_transition, -1);
|
||
|
int fromId = a.getResourceId(R.styleable.TransitionManager_fromScene, -1);
|
||
|
Scene fromScene = (fromId < 0) ? null: Scene.getSceneForLayout(sceneRoot, fromId, mContext);
|
||
|
int toId = a.getResourceId(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();
|
||
|
}
|
||
|
}
|