BridgeInflater.java revision 54b6cfa9a9e5b861a9930af873580d6dc20f773c
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 @SuppressWarnings("unchecked") 70 @Override 71 public View onCreateView(String name, AttributeSet attrs) throws ClassNotFoundException { 72 View view = null; 73 74 try { 75 // First try to find a class using the default Android prefixes 76 for (String prefix : sClassPrefixList) { 77 try { 78 view = createView(name, prefix, attrs); 79 if (view != null) { 80 break; 81 } 82 } catch (ClassNotFoundException e) { 83 // Ignore. We'll try again using the base class below. 84 } 85 } 86 87 // Next try using the parent loader. This will most likely only work for 88 // fully-qualified class names. 89 try { 90 if (view == null) { 91 view = super.onCreateView(name, attrs); 92 } 93 } catch (ClassNotFoundException e) { 94 // Ignore. We'll try again using the custom view loader below. 95 } 96 97 // Finally try again using the custom view loader 98 try { 99 if (view == null) { 100 view = loadCustomView(name, attrs); 101 } 102 } catch (ClassNotFoundException e) { 103 // If the class was not found, we throw the exception directly, because this 104 // method is already expected to throw it. 105 throw e; 106 } 107 } catch (Exception e) { 108 // Wrap the real exception in a ClassNotFoundException, so that the calling method 109 // can deal with it. 110 ClassNotFoundException exception = new ClassNotFoundException("onCreateView", e); 111 throw exception; 112 } 113 114 setupViewInContext(view, attrs); 115 116 return view; 117 } 118 119 @Override 120 public View createViewFromTag(String name, AttributeSet attrs) { 121 View view = null; 122 try { 123 view = super.createViewFromTag(name, attrs); 124 } catch (InflateException e) { 125 // try to load the class from using the custom view loader 126 try { 127 view = loadCustomView(name, attrs); 128 } catch (Exception e2) { 129 // Wrap the real exception in an InflateException so that the calling 130 // method can deal with it. 131 InflateException exception = new InflateException(); 132 if (e2.getClass().equals(ClassNotFoundException.class) == false) { 133 exception.initCause(e2); 134 } else { 135 exception.initCause(e); 136 } 137 throw exception; 138 } 139 } 140 141 setupViewInContext(view, attrs); 142 143 return view; 144 } 145 146 @Override 147 public View inflate(int resource, ViewGroup root) { 148 Context context = getContext(); 149 if (context instanceof BridgeContext) { 150 BridgeContext bridgeContext = (BridgeContext)context; 151 152 IResourceValue value = null; 153 154 String[] layoutInfo = Bridge.resolveResourceValue(resource); 155 if (layoutInfo != null) { 156 value = bridgeContext.getFrameworkResource(BridgeConstants.RES_LAYOUT, 157 layoutInfo[0]); 158 } else { 159 layoutInfo = mProjectCallback.resolveResourceValue(resource); 160 161 if (layoutInfo != null) { 162 value = bridgeContext.getProjectResource(BridgeConstants.RES_LAYOUT, 163 layoutInfo[0]); 164 } 165 } 166 167 if (value != null) { 168 File f = new File(value.getValue()); 169 if (f.isFile()) { 170 try { 171 KXmlParser parser = new KXmlParser(); 172 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 173 parser.setInput(new FileReader(f)); 174 175 BridgeXmlBlockParser bridgeParser = new BridgeXmlBlockParser( 176 parser, bridgeContext, false); 177 178 return inflate(bridgeParser, root); 179 } catch (Exception e) { 180 bridgeContext.getLogger().error(e); 181 // return null below. 182 } 183 } 184 } 185 } 186 return null; 187 } 188 189 private View loadCustomView(String name, AttributeSet attrs) throws ClassNotFoundException, 190 Exception{ 191 if (mProjectCallback != null) { 192 // first get the classname in case it's not the node name 193 if (name.equals("view")) { 194 name = attrs.getAttributeValue(null, "class"); 195 } 196 197 mConstructorArgs[1] = attrs; 198 199 Object customView = mProjectCallback.loadView(name, mConstructorSignature, 200 mConstructorArgs); 201 202 if (customView instanceof View) { 203 return (View)customView; 204 } 205 } 206 207 return null; 208 } 209 210 211 212 private void setupViewInContext(View view, AttributeSet attrs) { 213 if (getContext() instanceof BridgeContext) { 214 BridgeContext bc = (BridgeContext) getContext(); 215 if (attrs instanceof BridgeXmlBlockParser) { 216 Object viewKey = ((BridgeXmlBlockParser) attrs).getViewKey(); 217 if (viewKey != null) { 218 bc.addViewKey(view, viewKey); 219 } 220 } 221 } 222 } 223 224 @Override 225 public LayoutInflater cloneInContext(Context newContext) { 226 return new BridgeInflater(this, newContext); 227 } 228} 229