18158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki/*
28158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * Copyright (C) 2017 The Android Open Source Project
38158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki *
48158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * Licensed under the Apache License, Version 2.0 (the "License");
58158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * you may not use this file except in compliance with the License.
68158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * You may obtain a copy of the License at
78158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki *
88158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki *      http://www.apache.org/licenses/LICENSE-2.0
98158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki *
108158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * Unless required by applicable law or agreed to in writing, software
118158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * distributed under the License is distributed on an "AS IS" BASIS,
128158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * See the License for the specific language governing permissions and
148158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * limitations under the License.
158158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki */
168158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
17ac5fe7c617c66850fff75a9fce9979c6e5674b0fAurimas Liutikaspackage androidx.transition;
188158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
198158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.content.Context;
208158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.content.res.Resources;
218158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.content.res.TypedArray;
228158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.content.res.XmlResourceParser;
238158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.util.AttributeSet;
248158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.util.Xml;
258158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.view.InflateException;
268158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport android.view.ViewGroup;
278158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
284d1d6a17310a57807dabb3f404715cfe43a90ed0Aurimas Liutikasimport androidx.annotation.NonNull;
294d1d6a17310a57807dabb3f404715cfe43a90ed0Aurimas Liutikasimport androidx.collection.ArrayMap;
304d1d6a17310a57807dabb3f404715cfe43a90ed0Aurimas Liutikasimport androidx.core.content.res.TypedArrayUtils;
314d1d6a17310a57807dabb3f404715cfe43a90ed0Aurimas Liutikas
328158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport org.xmlpull.v1.XmlPullParser;
338158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport org.xmlpull.v1.XmlPullParserException;
348158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
358158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport java.io.IOException;
368158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakiimport java.lang.reflect.Constructor;
378158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
388158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki/**
398158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki * This class inflates scenes and transitions from resource files.
408158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki */
418158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Arakipublic class TransitionInflater {
428158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
438158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private static final Class<?>[] CONSTRUCTOR_SIGNATURE =
448158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            new Class[]{Context.class, AttributeSet.class};
458158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private static final ArrayMap<String, Constructor> CONSTRUCTORS = new ArrayMap<>();
468158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
478158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private final Context mContext;
488158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
498158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private TransitionInflater(@NonNull Context context) {
508158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        mContext = context;
518158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
528158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
538158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    /**
548158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * Obtains the TransitionInflater from the given context.
558158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     */
568158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    public static TransitionInflater from(Context context) {
578158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        return new TransitionInflater(context);
588158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
598158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
608158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    /**
618158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * Loads a {@link Transition} object from a resource
628158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     *
638158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @param resource The resource id of the transition to load
648158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @return The loaded Transition object
658158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @throws android.content.res.Resources.NotFoundException when the
668158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     *                                                         transition cannot be loaded
678158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     */
688158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    public Transition inflateTransition(int resource) {
698158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        XmlResourceParser parser = mContext.getResources().getXml(resource);
708158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        try {
718158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            return createTransitionFromXml(parser, Xml.asAttributeSet(parser), null);
728158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } catch (XmlPullParserException e) {
738158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            throw new InflateException(e.getMessage(), e);
748158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } catch (IOException e) {
758158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            throw new InflateException(
768158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    parser.getPositionDescription() + ": " + e.getMessage(), e);
778158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } finally {
788158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            parser.close();
798158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
808158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
818158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
828158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    /**
838158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * Loads a {@link TransitionManager} object from a resource
848158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     *
858158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @param resource The resource id of the transition manager to load
868158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @return The loaded TransitionManager object
878158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     * @throws android.content.res.Resources.NotFoundException when the
888158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     *                                                         transition manager cannot be loaded
898158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki     */
908158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    public TransitionManager inflateTransitionManager(int resource, ViewGroup sceneRoot) {
918158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        XmlResourceParser parser = mContext.getResources().getXml(resource);
928158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        try {
938158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            return createTransitionManagerFromXml(parser, Xml.asAttributeSet(parser), sceneRoot);
948158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } catch (XmlPullParserException e) {
958158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            InflateException ex = new InflateException(e.getMessage());
968158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            ex.initCause(e);
978158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            throw ex;
988158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } catch (IOException e) {
998158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            InflateException ex = new InflateException(
1008158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    parser.getPositionDescription()
1018158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                            + ": " + e.getMessage());
1028158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            ex.initCause(e);
1038158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            throw ex;
1048158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } finally {
1058158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            parser.close();
1068158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
1078158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
1088158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1098158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    //
1108158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    // Transition loading
1118158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    //
1128158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private Transition createTransitionFromXml(XmlPullParser parser,
1138158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            AttributeSet attrs, Transition parent)
1148158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            throws XmlPullParserException, IOException {
1158158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1168158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        Transition transition = null;
1178158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1188158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        // Make sure we are on a start tag.
1198158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int type;
1208158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int depth = parser.getDepth();
1218158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1228158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        TransitionSet transitionSet = (parent instanceof TransitionSet)
1238158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                ? (TransitionSet) parent : null;
1248158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1258158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
1268158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                && type != XmlPullParser.END_DOCUMENT) {
1278158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1288158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if (type != XmlPullParser.START_TAG) {
1298158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                continue;
1308158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
1318158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1328158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            String name = parser.getName();
1338158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if ("fade".equals(name)) {
1348158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                transition = new Fade(mContext, attrs);
1358158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else if ("changeBounds".equals(name)) {
1368158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                transition = new ChangeBounds(mContext, attrs);
137ae4925aede60914bcca5ed47d7ce868ae14313a2Yuichi Araki            } else if ("slide".equals(name)) {
138ae4925aede60914bcca5ed47d7ce868ae14313a2Yuichi Araki                transition = new Slide(mContext, attrs);
1394c904b38c763179727b9b5ea7a80454630545663Yuichi Araki            } else if ("explode".equals(name)) {
1404c904b38c763179727b9b5ea7a80454630545663Yuichi Araki                transition = new Explode(mContext, attrs);
141fdfa819aa29e12734eb90101f5f40d2636a5f1aeYuichi Araki            } else if ("changeImageTransform".equals(name)) {
142fdfa819aa29e12734eb90101f5f40d2636a5f1aeYuichi Araki                transition = new ChangeImageTransform(mContext, attrs);
14350ce74b0963de66b294761c045428562d946b65aYuichi Araki            } else if ("changeTransform".equals(name)) {
14450ce74b0963de66b294761c045428562d946b65aYuichi Araki                transition = new ChangeTransform(mContext, attrs);
145f137a45c77ee2e753262dc3c5f691fdcfd75053bYuichi Araki            } else if ("changeClipBounds".equals(name)) {
146f137a45c77ee2e753262dc3c5f691fdcfd75053bYuichi Araki                transition = new ChangeClipBounds(mContext, attrs);
1478158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else if ("autoTransition".equals(name)) {
1488158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                transition = new AutoTransition(mContext, attrs);
149913cfb35ee8a1fc3223c4f98bd1e22182b035186Yuichi Araki            } else if ("changeScroll".equals(name)) {
150913cfb35ee8a1fc3223c4f98bd1e22182b035186Yuichi Araki                transition = new ChangeScroll(mContext, attrs);
1518158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else if ("transitionSet".equals(name)) {
1528158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                transition = new TransitionSet(mContext, attrs);
1538158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else if ("transition".equals(name)) {
1548158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                transition = (Transition) createCustom(attrs, Transition.class, "transition");
1558158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else if ("targets".equals(name)) {
1568158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                getTargetIds(parser, attrs, parent);
157142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki            } else if ("arcMotion".equals(name)) {
158142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                if (parent == null) {
159142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                    throw new RuntimeException("Invalid use of arcMotion element");
160142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                }
161142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                parent.setPathMotion(new ArcMotion(mContext, attrs));
162142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki            } else if ("pathMotion".equals(name)) {
163142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                if (parent == null) {
164142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                    throw new RuntimeException("Invalid use of pathMotion element");
165142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                }
166142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                parent.setPathMotion((PathMotion) createCustom(attrs, PathMotion.class,
167142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                        "pathMotion"));
168142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki            } else if ("patternPathMotion".equals(name)) {
169142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                if (parent == null) {
170142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                    throw new RuntimeException("Invalid use of patternPathMotion element");
171142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                }
172142a5654b5eff546b6fa1b9b938896356ff9b03bYuichi Araki                parent.setPathMotion(new PatternPathMotion(mContext, attrs));
1738158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else {
1748158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                throw new RuntimeException("Unknown scene name: " + parser.getName());
1758158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
1768158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if (transition != null) {
1778158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                if (!parser.isEmptyElementTag()) {
1788158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    createTransitionFromXml(parser, attrs, transition);
1798158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                }
1808158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                if (transitionSet != null) {
1818158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transitionSet.addTransition(transition);
1828158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transition = null;
1838158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                } else if (parent != null) {
1848158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    throw new InflateException("Could not add transition to another transition.");
1858158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                }
1868158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
1878158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
1888158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1898158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        return transition;
1908158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
1918158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1928158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private Object createCustom(AttributeSet attrs, Class expectedType, String tag) {
1938158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        String className = attrs.getAttributeValue(null, "class");
1948158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1958158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        if (className == null) {
1968158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            throw new InflateException(tag + " tag must have a 'class' attribute");
1978158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
1988158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
1998158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        try {
2008158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            synchronized (CONSTRUCTORS) {
2018158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                Constructor constructor = CONSTRUCTORS.get(className);
2028158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                if (constructor == null) {
203df81a97346c6617a3de1f54d7d13eecd5a3200eeYuichi Araki                    @SuppressWarnings("unchecked")
2048158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    Class<?> c = mContext.getClassLoader().loadClass(className)
2058158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                            .asSubclass(expectedType);
2068158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    if (c != null) {
2078158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        constructor = c.getConstructor(CONSTRUCTOR_SIGNATURE);
2088158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        constructor.setAccessible(true);
2098158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        CONSTRUCTORS.put(className, constructor);
2108158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    }
2118158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                }
2128158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                //noinspection ConstantConditions
2138158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                return constructor.newInstance(mContext, attrs);
2148158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
2158158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        } catch (Exception e) {
2168158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            throw new InflateException("Could not instantiate " + expectedType + " class "
2178158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    + className, e);
2188158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
2198158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
2208158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2218158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private void getTargetIds(XmlPullParser parser,
2228158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            AttributeSet attrs, Transition transition) throws XmlPullParserException, IOException {
2238158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2248158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        // Make sure we are on a start tag.
2258158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int type;
2268158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int depth = parser.getDepth();
2278158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2288158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
2298158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                && type != XmlPullParser.END_DOCUMENT) {
2308158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2318158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if (type != XmlPullParser.START_TAG) {
2328158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                continue;
2338158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
2348158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2358158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            String name = parser.getName();
2368158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if (name.equals("target")) {
2378158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                TypedArray a = mContext.obtainStyledAttributes(attrs, Styleable.TRANSITION_TARGET);
2388158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                int id = TypedArrayUtils.getNamedResourceId(a, parser, "targetId",
2398158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        Styleable.TransitionTarget.TARGET_ID, 0);
2408158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                String transitionName;
2418158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                if (id != 0) {
2428158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transition.addTarget(id);
2438158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                } else if ((id = TypedArrayUtils.getNamedResourceId(a, parser, "excludeId",
2448158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        Styleable.TransitionTarget.EXCLUDE_ID, 0)) != 0) {
2458158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transition.excludeTarget(id, true);
2468158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                } else if ((transitionName = TypedArrayUtils.getNamedString(a, parser, "targetName",
2478158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        Styleable.TransitionTarget.TARGET_NAME)) != null) {
2488158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transition.addTarget(transitionName);
2498158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                } else if ((transitionName = TypedArrayUtils.getNamedString(a, parser,
2508158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        "excludeName", Styleable.TransitionTarget.EXCLUDE_NAME)) != null) {
2518158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transition.excludeTarget(transitionName, true);
2528158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                } else {
2538158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    String className = TypedArrayUtils.getNamedString(a, parser,
2548158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                            "excludeClass", Styleable.TransitionTarget.EXCLUDE_CLASS);
2558158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    try {
2568158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        if (className != null) {
2578158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                            Class clazz = Class.forName(className);
2588158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                            transition.excludeTarget(clazz, true);
2598158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        } else if ((className = TypedArrayUtils.getNamedString(a, parser,
2608158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                                "targetClass", Styleable.TransitionTarget.TARGET_CLASS)) != null) {
2618158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                            Class clazz = Class.forName(className);
2628158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                            transition.addTarget(clazz);
2638158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        }
2648158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    } catch (ClassNotFoundException e) {
2658158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        a.recycle();
2668158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                        throw new RuntimeException("Could not create " + className, e);
2678158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    }
2688158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                }
2698158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                a.recycle();
2708158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else {
2718158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                throw new RuntimeException("Unknown scene name: " + parser.getName());
2728158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
2738158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
2748158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
2758158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2768158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    //
2778158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    // TransitionManager loading
2788158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    //
2798158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2808158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private TransitionManager createTransitionManagerFromXml(XmlPullParser parser,
2818158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            AttributeSet attrs, ViewGroup sceneRoot) throws XmlPullParserException, IOException {
2828158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2838158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        // Make sure we are on a start tag.
2848158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int type;
2858158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int depth = parser.getDepth();
2868158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        TransitionManager transitionManager = null;
2878158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2888158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        while (((type = parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
2898158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                && type != XmlPullParser.END_DOCUMENT) {
2908158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2918158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if (type != XmlPullParser.START_TAG) {
2928158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                continue;
2938158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
2948158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
2958158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            String name = parser.getName();
2968158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if (name.equals("transitionManager")) {
2978158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                transitionManager = new TransitionManager();
2988158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else if (name.equals("transition") && (transitionManager != null)) {
2998158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                loadTransition(attrs, parser, sceneRoot, transitionManager);
3008158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            } else {
3018158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                throw new RuntimeException("Unknown scene name: " + parser.getName());
3028158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
3038158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
3048158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        return transitionManager;
3058158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
3068158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
3078158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    private void loadTransition(AttributeSet attrs, XmlPullParser parser, ViewGroup sceneRoot,
3088158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            TransitionManager transitionManager) throws Resources.NotFoundException {
3098158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
3108158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        TypedArray a = mContext.obtainStyledAttributes(attrs, Styleable.TRANSITION_MANAGER);
3118158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int transitionId = TypedArrayUtils.getNamedResourceId(a, parser, "transition",
3128158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                Styleable.TransitionManager.TRANSITION, -1);
3138158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int fromId = TypedArrayUtils.getNamedResourceId(a, parser, "fromScene",
3148158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                Styleable.TransitionManager.FROM_SCENE, -1);
3158158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        Scene fromScene = (fromId < 0) ? null : Scene.getSceneForLayout(sceneRoot, fromId,
3168158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                mContext);
3178158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        int toId = TypedArrayUtils.getNamedResourceId(a, parser, "toScene",
3188158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                Styleable.TransitionManager.TO_SCENE, -1);
3198158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        Scene toScene = (toId < 0) ? null : Scene.getSceneForLayout(sceneRoot, toId, mContext);
3208158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
3218158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        if (transitionId >= 0) {
3228158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            Transition transition = inflateTransition(transitionId);
3238158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            if (transition != null) {
3248158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                if (toScene == null) {
3258158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    throw new RuntimeException("No toScene for transition ID " + transitionId);
3268158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                }
3278158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                if (fromScene == null) {
3288158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transitionManager.setTransition(toScene, transition);
3298158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                } else {
3308158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                    transitionManager.setTransition(fromScene, toScene, transition);
3318158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki                }
3328158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki            }
3338158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        }
3348158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki        a.recycle();
3358158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki    }
3368158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki
3378158051cdfef95fc1f22b56bba93b9c610f5ecb1Yuichi Araki}
338