/*
 * 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 Transition.Transition, TransitionSet.TransitionSet, TransitionTarget.TransitionTarget, Fade.Fade, and TransitionManager.TransitionManager.
/** * 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.
/** * Obtains the TransitionInflater from the given context. */
public static TransitionInflater from(Context context) { return new TransitionInflater(context); }
Loads a Transition object from a resource
Params:
  • resource – The resource id of the transition to load
Throws:
Returns:The loaded Transition object
/** * 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 TransitionManager object from a resource
Params:
  • resource – The resource id of the transition manager to load
Throws:
Returns:The loaded TransitionManager object
/** * 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(); } }