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