BridgeInflater.java revision f013e1afd1e68af5e3b868c26a653bbfb39538f8
1/*
2 * Copyright (C) 2008 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 */
16
17package android.view;
18
19import com.android.layoutlib.api.IProjectCallback;
20import com.android.layoutlib.api.IResourceValue;
21import com.android.layoutlib.bridge.Bridge;
22import com.android.layoutlib.bridge.BridgeConstants;
23import com.android.layoutlib.bridge.BridgeContext;
24import com.android.layoutlib.bridge.BridgeXmlBlockParser;
25
26import org.kxml2.io.KXmlParser;
27import org.xmlpull.v1.XmlPullParser;
28
29import android.content.Context;
30import android.util.AttributeSet;
31
32import java.io.File;
33import java.io.FileReader;
34
35/**
36 * Custom implementation of {@link LayoutInflater} to handle custom views.
37 */
38public final class BridgeInflater extends LayoutInflater {
39
40    private final IProjectCallback mProjectCallback;
41
42    /**
43     * List of class prefixes which are tried first by default.
44     * <p/>
45     * This should match the list in com.android.internal.policy.impl.PhoneLayoutInflater.
46     */
47    private static final String[] sClassPrefixList = {
48        "android.widget.",
49        "android.webkit."
50    };
51
52    protected BridgeInflater(LayoutInflater original, Context newContext) {
53        super(original, newContext);
54        mProjectCallback = null;
55    }
56
57    /**
58     * Instantiate a new BridgeInflater with an {@link IProjectCallback} object.
59     *
60     * @param context The Android application context.
61     * @param projectCallback the {@link IProjectCallback} object.
62     */
63    public BridgeInflater(Context context, IProjectCallback projectCallback) {
64        super(context);
65        mProjectCallback = projectCallback;
66        mConstructorArgs[0] = context;
67    }
68
69    @Override
70    public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
71        View view = null;
72
73        try {
74            // First try to find a class using the default Android prefixes
75            for (String prefix : sClassPrefixList) {
76                try {
77                    view = createView(name, prefix, attrs);
78                    if (view != null) {
79                        break;
80                    }
81                } catch (ClassNotFoundException e) {
82                    // Ignore. We'll try again using the base class below.
83                }
84            }
85
86            // Next try using the parent loader. This will most likely only work for
87            // fully-qualified class names.
88            try {
89                if (view == null) {
90                    view = super.onCreateView(name, attrs);
91                }
92            } catch (ClassNotFoundException e) {
93                // Ignore. We'll try again using the custom view loader below.
94            }
95
96            // Finally try again using the custom view loader
97            try {
98                if (view == null) {
99                    view = loadCustomView(name, attrs);
100                }
101            } catch (ClassNotFoundException e) {
102                // If the class was not found, we throw the exception directly, because this
103                // method is already expected to throw it.
104                throw e;
105            }
106        } catch (Exception e) {
107            // Wrap the real exception in a ClassNotFoundException, so that the calling method
108            // can deal with it.
109            ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
110            throw exception;
111        }
112
113        setupViewInContext(view, attrs);
114
115        return view;
116    }
117
118    @Override
119    public View createViewFromTag(String name, AttributeSet attrs) {
120        View view = null;
121        try {
122            view = super.createViewFromTag(name, attrs);
123        } catch (InflateException e) {
124            // try to load the class from using the custom view loader
125            try {
126                view = loadCustomView(name, attrs);
127            } catch (Exception e2) {
128                // Wrap the real exception in an InflateException so that the calling
129                // method can deal with it.
130                InflateException exception = new InflateException();
131                if (e2.getClass().equals(ClassNotFoundException.class) == false) {
132                    exception.initCause(e2);
133                } else {
134                    exception.initCause(e);
135                }
136                throw exception;
137            }
138        }
139
140        setupViewInContext(view, attrs);
141
142        return view;
143    }
144
145    @Override
146    public View inflate(int resource, ViewGroup root) {
147        Context context = getContext();
148        if (context instanceof BridgeContext) {
149            BridgeContext bridgeContext = (BridgeContext)context;
150
151            IResourceValue value = null;
152
153            String[] layoutInfo = Bridge.resolveResourceValue(resource);
154            if (layoutInfo != null) {
155                value = bridgeContext.getFrameworkResource(BridgeConstants.RES_LAYOUT,
156                        layoutInfo[0]);
157            } else {
158                layoutInfo = mProjectCallback.resolveResourceValue(resource);
159
160                if (layoutInfo != null) {
161                    value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT,
162                            layoutInfo[0]);
163                }
164            }
165
166            if (value != null) {
167                File f = new File(value.getValue());
168                if (f.isFile()) {
169                    try {
170                        KXmlParser parser = new KXmlParser();
171                        parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
172                        parser.setInput(new FileReader(f));
173
174                        BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
175                                parser, bridgeContext, false);
176
177                        return inflate(bridgeParser, root);
178                    } catch (Exception e) {
179                        bridgeContext.getLogger().error(e);
180                        // return null below.
181                    }
182                }
183            }
184        }
185        return null;
186    }
187
188    private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
189            Exception{
190        if (mProjectCallback != null) {
191            // first get the classname in case it's not the node name
192            if (name.equals("view")) {
193                name = attrs.getAttributeValue(null, "class");
194            }
195
196            mConstructorArgs[1] = attrs;
197
198            Object customView = mProjectCallback.loadView(name, mConstructorSignature,
199                    mConstructorArgs);
200
201            if (customView instanceof View) {
202                return (View)customView;
203            }
204        }
205
206        return null;
207    }
208
209
210
211    private void setupViewInContext(View view, AttributeSet attrs) {
212        if (getContext() instanceof BridgeContext) {
213            BridgeContext bc = (BridgeContext) getContext();
214            if (attrs instanceof BridgeXmlBlockParser) {
215                Object viewKey = ((BridgeXmlBlockParser) attrs).getViewKey();
216                if (viewKey != null) {
217                    bc.addViewKey(view, viewKey);
218                }
219            }
220        }
221    }
222
223    @Override
224    public LayoutInflater cloneInContext(Context newContext) {
225        return new BridgeInflater(this, newContext);
226    }
227}
228