blob: 941f1ce6ef228abe718a2a11891580a1e9038953 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
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
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -070017package android.view;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Xavier Ducrohet19a02102010-12-15 19:20:08 -080019import com.android.ide.common.rendering.api.IProjectCallback;
Xavier Ducrohet918aaa52011-01-13 10:59:34 -080020import com.android.ide.common.rendering.api.LayoutLog;
Xavier Ducrohet82b92322011-01-24 14:03:21 -080021import com.android.ide.common.rendering.api.MergeCookie;
Xavier Ducrohet2fae8582011-03-29 11:58:02 -070022import com.android.ide.common.rendering.api.ResourceReference;
Xavier Ducrohet19a02102010-12-15 19:20:08 -080023import com.android.ide.common.rendering.api.ResourceValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import com.android.layoutlib.bridge.Bridge;
Xavier Ducrohet7f9f99ea2011-08-11 10:16:17 -070025import com.android.layoutlib.bridge.android.BridgeContext;
26import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
Xavier Ducrohet04ce8112011-06-09 19:14:06 -070027import com.android.layoutlib.bridge.impl.ParserFactory;
Xavier Ducrohet35ea7cd2011-01-28 11:44:21 -080028import com.android.resources.ResourceType;
Xavier Ducrohetb3534952011-01-28 12:46:12 -080029import com.android.util.Pair;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import org.xmlpull.v1.XmlPullParser;
32
33import android.content.Context;
34import android.util.AttributeSet;
Xavier Ducrohetc2e96512010-11-09 18:25:03 -080035import android.view.InflateException;
36import android.view.LayoutInflater;
37import android.view.View;
38import android.view.ViewGroup;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
40import java.io.File;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42/**
Xavier Ducrohet55acd602010-11-18 22:12:34 -080043 * Custom implementation of {@link LayoutInflater} to handle custom views.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044 */
45public final class BridgeInflater extends LayoutInflater {
Xavier Ducrohet55acd602010-11-18 22:12:34 -080046
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047 private final IProjectCallback mProjectCallback;
Xavier Ducrohet82b92322011-01-24 14:03:21 -080048 private boolean mIsInMerge = false;
Xavier Ducrohet2fae8582011-03-29 11:58:02 -070049 private ResourceReference mResourceReference;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050
51 /**
52 * List of class prefixes which are tried first by default.
53 * <p/>
54 * This should match the list in com.android.internal.policy.impl.PhoneLayoutInflater.
Xavier Ducrohet55acd602010-11-18 22:12:34 -080055 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056 private static final String[] sClassPrefixList = {
57 "android.widget.",
58 "android.webkit."
59 };
60
61 protected BridgeInflater(LayoutInflater original, Context newContext) {
62 super(original, newContext);
63 mProjectCallback = null;
64 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -080065
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 /**
67 * Instantiate a new BridgeInflater with an {@link IProjectCallback} object.
Xavier Ducrohet55acd602010-11-18 22:12:34 -080068 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 * @param context The Android application context.
70 * @param projectCallback the {@link IProjectCallback} object.
71 */
72 public BridgeInflater(Context context, IProjectCallback projectCallback) {
73 super(context);
74 mProjectCallback = projectCallback;
75 mConstructorArgs[0] = context;
76 }
77
78 @Override
79 public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException {
80 View view = null;
81
82 try {
83 // First try to find a class using the default Android prefixes
84 for (String prefix : sClassPrefixList) {
85 try {
86 view = createView(name, prefix, attrs);
87 if (view != null) {
88 break;
89 }
90 } catch (ClassNotFoundException e) {
91 // Ignore. We'll try again using the base class below.
92 }
93 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -080094
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 // Next try using the parent loader. This will most likely only work for
96 // fully-qualified class names.
97 try {
98 if (view == null) {
99 view = super.onCreateView(name, attrs);
100 }
101 } catch (ClassNotFoundException e) {
102 // Ignore. We'll try again using the custom view loader below.
103 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105 // Finally try again using the custom view loader
106 try {
107 if (view == null) {
108 view = loadCustomView(name, attrs);
109 }
110 } catch (ClassNotFoundException e) {
111 // If the class was not found, we throw the exception directly, because this
112 // method is already expected to throw it.
113 throw e;
114 }
115 } catch (Exception e) {
116 // Wrap the real exception in a ClassNotFoundException, so that the calling method
117 // can deal with it.
118 ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e);
119 throw exception;
120 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800121
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122 setupViewInContext(view, attrs);
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 return view;
125 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800126
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 @Override
Dianne Hackborn625ac272010-09-17 18:29:22 -0700128 public View createViewFromTag(View parent, String name, AttributeSet attrs) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 View view = null;
130 try {
Dianne Hackborn625ac272010-09-17 18:29:22 -0700131 view = super.createViewFromTag(parent, name, attrs);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 } catch (InflateException e) {
133 // try to load the class from using the custom view loader
134 try {
135 view = loadCustomView(name, attrs);
136 } catch (Exception e2) {
137 // Wrap the real exception in an InflateException so that the calling
138 // method can deal with it.
139 InflateException exception = new InflateException();
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800140 if (e2.getClass().equals(ClassNotFoundException.class) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 exception.initCause(e2);
142 } else {
143 exception.initCause(e);
144 }
145 throw exception;
146 }
147 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800148
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800149 setupViewInContext(view, attrs);
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 return view;
152 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800153
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154 @Override
155 public View inflate(int resource, ViewGroup root) {
156 Context context = getContext();
157 if (context instanceof BridgeContext) {
158 BridgeContext bridgeContext = (BridgeContext)context;
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800159
Xavier Ducrohetffb42f62010-12-09 18:26:01 -0800160 ResourceValue value = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161
Xavier Ducrohetb3534952011-01-28 12:46:12 -0800162 Pair<ResourceType, String> layoutInfo = Bridge.resolveResourceId(resource);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 if (layoutInfo != null) {
Xavier Ducrohet70552fb2011-01-17 13:49:12 -0800164 value = bridgeContext.getRenderResources().getFrameworkResource(
Xavier Ducrohetb3534952011-01-28 12:46:12 -0800165 ResourceType.LAYOUT, layoutInfo.getSecond());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 } else {
Xavier Ducrohetb3534952011-01-28 12:46:12 -0800167 layoutInfo = mProjectCallback.resolveResourceId(resource);
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 if (layoutInfo != null) {
Xavier Ducrohet70552fb2011-01-17 13:49:12 -0800170 value = bridgeContext.getRenderResources().getProjectResource(
Xavier Ducrohetb3534952011-01-28 12:46:12 -0800171 ResourceType.LAYOUT, layoutInfo.getSecond());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800172 }
173 }
174
175 if (value != null) {
176 File f = new File(value.getValue());
177 if (f.isFile()) {
178 try {
Xavier Ducrohet04ce8112011-06-09 19:14:06 -0700179 XmlPullParser parser = ParserFactory.create(f);
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800180
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800181 BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser(
182 parser, bridgeContext, false);
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 return inflate(bridgeParser, root);
185 } catch (Exception e) {
Xavier Ducrohet918aaa52011-01-13 10:59:34 -0800186 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
Xavier Ducrohet51a7e542011-01-14 16:40:43 -0800187 "Failed to parse file " + f.getAbsolutePath(), e, null /*data*/);
Xavier Ducrohet6c740cf2011-01-11 13:19:02 -0800188
189 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800190 }
191 }
192 }
193 }
194 return null;
195 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800196
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800197 private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException,
198 Exception{
199 if (mProjectCallback != null) {
200 // first get the classname in case it's not the node name
201 if (name.equals("view")) {
202 name = attrs.getAttributeValue(null, "class");
203 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 mConstructorArgs[1] = attrs;
206
207 Object customView = mProjectCallback.loadView(name, mConstructorSignature,
208 mConstructorArgs);
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800209
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 if (customView instanceof View) {
211 return (View)customView;
212 }
213 }
214
215 return null;
216 }
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218 private void setupViewInContext(View view, AttributeSet attrs) {
219 if (getContext() instanceof BridgeContext) {
220 BridgeContext bc = (BridgeContext) getContext();
221 if (attrs instanceof BridgeXmlBlockParser) {
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800222 BridgeXmlBlockParser parser = (BridgeXmlBlockParser) attrs;
223
224 // get the view key
Xavier Ducrohet19a02102010-12-15 19:20:08 -0800225 Object viewKey = parser.getViewCookie();
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800226
Xavier Ducrohet2fae8582011-03-29 11:58:02 -0700227 if (viewKey == null) {
228 int currentDepth = parser.getDepth();
229
230 // test whether we are in an included file or in a adapter binding view.
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800231 BridgeXmlBlockParser previousParser = bc.getPreviousParser();
232 if (previousParser != null) {
Xavier Ducrohet2fae8582011-03-29 11:58:02 -0700233 // looks like we inside an embedded layout.
234 // only apply the cookie of the calling node (<include>) if we are at the
235 // top level of the embedded layout. If there is a merge tag, then
236 // skip it and look for the 2nd level
237 int testDepth = mIsInMerge ? 2 : 1;
238 if (currentDepth == testDepth) {
239 viewKey = previousParser.getViewCookie();
240 // if we are in a merge, wrap the cookie in a MergeCookie.
241 if (viewKey != null && mIsInMerge) {
242 viewKey = new MergeCookie(viewKey);
243 }
244 }
245 } else if (mResourceReference != null && currentDepth == 1) {
246 // else if there's a resource reference, this means we are in an adapter
247 // binding case. Set the resource ref as the view cookie only for the top
248 // level view.
249 viewKey = mResourceReference;
Xavier Ducrohet55acd602010-11-18 22:12:34 -0800250 }
251 }
252
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253 if (viewKey != null) {
254 bc.addViewKey(view, viewKey);
255 }
256 }
257 }
258 }
259
Xavier Ducrohet82b92322011-01-24 14:03:21 -0800260 public void setIsInMerge(boolean isInMerge) {
261 mIsInMerge = isInMerge;
262 }
263
Xavier Ducrohet2fae8582011-03-29 11:58:02 -0700264 public void setResourceReference(ResourceReference reference) {
265 mResourceReference = reference;
266 }
267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800268 @Override
269 public LayoutInflater cloneInContext(Context newContext) {
270 return new BridgeInflater(this, newContext);
271 }
272}