Bridge.java revision 5a09488a158b669577cd8eb557ce4feb62929e75
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 com.android.layoutlib.bridge; 18 19import com.android.layoutlib.api.ILayoutLog; 20import com.android.layoutlib.api.IProjectCallback; 21import com.android.layoutlib.api.IResourceValue; 22import com.android.layoutlib.api.IXmlPullParser; 23import com.android.layoutlib.api.LayoutBridge; 24import com.android.layoutlib.api.SceneParams; 25import com.android.layoutlib.api.SceneResult; 26import com.android.layoutlib.bridge.android.BridgeAssetManager; 27import com.android.layoutlib.bridge.impl.FontLoader; 28import com.android.layoutlib.bridge.impl.LayoutSceneImpl; 29import com.android.ninepatch.NinePatch; 30import com.android.tools.layoutlib.create.MethodAdapter; 31import com.android.tools.layoutlib.create.OverrideMethod; 32 33import android.graphics.Bitmap; 34import android.graphics.Typeface_Delegate; 35import android.util.Finalizers; 36 37import java.lang.ref.SoftReference; 38import java.lang.reflect.Field; 39import java.lang.reflect.Modifier; 40import java.util.HashMap; 41import java.util.Map; 42 43/** 44 * Main entry point of the LayoutLib Bridge. 45 * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call 46 * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}. 47 */ 48public final class Bridge extends LayoutBridge { 49 50 public static class StaticMethodNotImplementedException extends RuntimeException { 51 private static final long serialVersionUID = 1L; 52 53 public StaticMethodNotImplementedException(String msg) { 54 super(msg); 55 } 56 } 57 58 /** 59 * Maps from id to resource name/type. This is for android.R only. 60 */ 61 private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>(); 62 /** 63 * Same as sRMap except for int[] instead of int resources. This is for android.R only. 64 */ 65 private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>(); 66 /** 67 * Reverse map compared to sRMap, resource type -> (resource name -> id). 68 * This is for android.R only. 69 */ 70 private final static Map<String, Map<String, Integer>> sRFullMap = 71 new HashMap<String, Map<String,Integer>>(); 72 73 private final static Map<Object, Map<String, SoftReference<Bitmap>>> sProjectBitmapCache = 74 new HashMap<Object, Map<String, SoftReference<Bitmap>>>(); 75 private final static Map<Object, Map<String, SoftReference<NinePatch>>> sProject9PatchCache = 76 new HashMap<Object, Map<String, SoftReference<NinePatch>>>(); 77 78 private final static Map<String, SoftReference<Bitmap>> sFrameworkBitmapCache = 79 new HashMap<String, SoftReference<Bitmap>>(); 80 private final static Map<String, SoftReference<NinePatch>> sFramework9PatchCache = 81 new HashMap<String, SoftReference<NinePatch>>(); 82 83 private static Map<String, Map<String, Integer>> sEnumValueMap; 84 85 /** 86 * A default logger than prints to stdout/stderr. 87 */ 88 private final static ILayoutLog sDefaultLogger = new ILayoutLog() { 89 public void error(String message) { 90 System.err.println(message); 91 } 92 93 public void error(Throwable t) { 94 String message = t.getMessage(); 95 if (message == null) { 96 message = t.getClass().getName(); 97 } 98 99 System.err.println(message); 100 } 101 102 public void warning(String message) { 103 System.out.println(message); 104 } 105 }; 106 107 @Override 108 public int getApiLevel() { 109 return LayoutBridge.API_CURRENT; 110 } 111 112 /* 113 * (non-Javadoc) 114 * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map) 115 */ 116 @Override 117 public boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { 118 sEnumValueMap = enumValueMap; 119 120 Finalizers.init(); 121 122 BridgeAssetManager.initSystem(); 123 124 125 // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener 126 // on static (native) methods which prints the signature on the console and 127 // throws an exception. 128 // This is useful when testing the rendering in ADT to identify static native 129 // methods that are ignored -- layoutlib_create makes them returns 0/false/null 130 // which is generally OK yet might be a problem, so this is how you'd find out. 131 // 132 // Currently layoutlib_create only overrides static native method. 133 // Static non-natives are not overridden and thus do not get here. 134 final String debug = System.getenv("DEBUG_LAYOUT"); 135 if (debug != null && !debug.equals("0") && !debug.equals("false")) { 136 137 OverrideMethod.setDefaultListener(new MethodAdapter() { 138 @Override 139 public void onInvokeV(String signature, boolean isNative, Object caller) { 140 sDefaultLogger.error("Missing Stub: " + signature + 141 (isNative ? " (native)" : "")); 142 143 if (debug.equalsIgnoreCase("throw")) { 144 // Throwing this exception doesn't seem that useful. It breaks 145 // the layout editor yet doesn't display anything meaningful to the 146 // user. Having the error in the console is just as useful. We'll 147 // throw it only if the environment variable is "throw" or "THROW". 148 throw new StaticMethodNotImplementedException(signature); 149 } 150 } 151 }); 152 } 153 154 // load the fonts. 155 FontLoader fontLoader = FontLoader.create(fontOsLocation); 156 if (fontLoader != null) { 157 Typeface_Delegate.init(fontLoader); 158 } else { 159 return false; 160 } 161 162 // now parse com.android.internal.R (and only this one as android.R is a subset of 163 // the internal version), and put the content in the maps. 164 try { 165 // WARNING: this only works because the class is already loaded, and therefore 166 // the objects returned by Field.get() are the same as the ones used by 167 // the code accessing the R class. 168 // int[] does not implement equals/hashCode, and if the parsing used a different class 169 // loader for the R class, this would NOT work. 170 Class<?> r = com.android.internal.R.class; 171 172 for (Class<?> inner : r.getDeclaredClasses()) { 173 String resType = inner.getSimpleName(); 174 175 Map<String, Integer> fullMap = new HashMap<String, Integer>(); 176 sRFullMap.put(resType, fullMap); 177 178 for (Field f : inner.getDeclaredFields()) { 179 // only process static final fields. Since the final attribute may have 180 // been altered by layoutlib_create, we only check static 181 int modifiers = f.getModifiers(); 182 if (Modifier.isStatic(modifiers)) { 183 Class<?> type = f.getType(); 184 if (type.isArray() && type.getComponentType() == int.class) { 185 // if the object is an int[] we put it in sRArrayMap 186 sRArrayMap.put((int[]) f.get(null), f.getName()); 187 } else if (type == int.class) { 188 Integer value = (Integer) f.get(null); 189 sRMap.put(value, new String[] { f.getName(), resType }); 190 fullMap.put(f.getName(), value); 191 } else { 192 assert false; 193 } 194 } 195 } 196 } 197 } catch (IllegalArgumentException e) { 198 // FIXME: log/return the error (there's no logger object at this point!) 199 e.printStackTrace(); 200 return false; 201 } catch (IllegalAccessException e) { 202 e.printStackTrace(); 203 return false; 204 } 205 206 return true; 207 } 208 209 @Override 210 public boolean dispose() { 211 BridgeAssetManager.clearSystem(); 212 return true; 213 } 214 215 /** 216 * Sets a 9 patch in a project cache or in the framework cache. 217 * @param value the path of the 9 patch 218 * @param ninePatch the 9 patch object 219 * @param projectKey the key of the project, or null to put the bitmap in the framework cache. 220 */ 221 public static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) { 222 if (projectKey != null) { 223 Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey); 224 225 if (map == null) { 226 map = new HashMap<String, SoftReference<NinePatch>>(); 227 sProject9PatchCache.put(projectKey, map); 228 } 229 230 map.put(value, new SoftReference<NinePatch>(ninePatch)); 231 } else { 232 sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch)); 233 } 234 } 235 236 /** 237 * Starts a layout session by inflating and rendering it. The method returns a 238 * {@link ILayoutScene} on which further actions can be taken. 239 * 240 * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the 241 * layout file. 242 * @param projectKey An Object identifying the project. This is used for the cache mechanism. 243 * @param screenWidth the screen width 244 * @param screenHeight the screen height 245 * @param renderFullSize if true, the rendering will render the full size needed by the 246 * layout. This size is never smaller than <var>screenWidth</var> x <var>screenHeight</var>. 247 * @param density the density factor for the screen. 248 * @param xdpi the screen actual dpi in X 249 * @param ydpi the screen actual dpi in Y 250 * @param themeName The name of the theme to use. 251 * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme. 252 * @param projectResources the resources of the project. The map contains (String, map) pairs 253 * where the string is the type of the resource reference used in the layout file, and the 254 * map contains (String, {@link IResourceValue}) pairs where the key is the resource name, 255 * and the value is the resource value. 256 * @param frameworkResources the framework resources. The map contains (String, map) pairs 257 * where the string is the type of the resource reference used in the layout file, and the map 258 * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the 259 * value is the resource value. 260 * @param projectCallback The {@link IProjectCallback} object to get information from 261 * the project. 262 * @param logger the object responsible for displaying warning/errors to the user. 263 * @return a new {@link ILayoutScene} object that contains the result of the layout. 264 * @since 5 265 */ 266 @Override 267 public BridgeLayoutScene createScene(SceneParams params) { 268 try { 269 SceneResult lastResult = SceneResult.SUCCESS; 270 LayoutSceneImpl scene = null; 271 synchronized (this) { 272 try { 273 scene = new LayoutSceneImpl(params); 274 275 scene.prepare(); 276 lastResult = scene.inflate(); 277 if (lastResult == SceneResult.SUCCESS) { 278 lastResult = scene.render(); 279 } 280 } finally { 281 if (scene != null) { 282 scene.cleanup(); 283 } 284 } 285 } 286 287 return new BridgeLayoutScene(this, scene, lastResult); 288 } catch (Throwable t) { 289 t.printStackTrace(); 290 return new BridgeLayoutScene(this, null, new SceneResult("error!", t)); 291 } 292 } 293 294 /* 295 * (non-Javadoc) 296 * @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object) 297 */ 298 @Override 299 public void clearCaches(Object projectKey) { 300 if (projectKey != null) { 301 sProjectBitmapCache.remove(projectKey); 302 sProject9PatchCache.remove(projectKey); 303 } 304 } 305 306 /** 307 * Returns details of a framework resource from its integer value. 308 * @param value the integer value 309 * @return an array of 2 strings containing the resource name and type, or null if the id 310 * does not match any resource. 311 */ 312 public static String[] resolveResourceValue(int value) { 313 return sRMap.get(value); 314 315 } 316 317 /** 318 * Returns the name of a framework resource whose value is an int array. 319 * @param array 320 */ 321 public static String resolveResourceValue(int[] array) { 322 return sRArrayMap.get(array); 323 } 324 325 /** 326 * Returns the integer id of a framework resource, from a given resource type and resource name. 327 * @param type the type of the resource 328 * @param name the name of the resource. 329 * @return an {@link Integer} containing the resource id, or null if no resource were found. 330 */ 331 public static Integer getResourceValue(String type, String name) { 332 Map<String, Integer> map = sRFullMap.get(type); 333 if (map != null) { 334 return map.get(name); 335 } 336 337 return null; 338 } 339 340 /** 341 * Returns the list of possible enums for a given attribute name. 342 */ 343 public static Map<String, Integer> getEnumValues(String attributeName) { 344 if (sEnumValueMap != null) { 345 return sEnumValueMap.get(attributeName); 346 } 347 348 return null; 349 } 350 351 /** 352 * Returns the bitmap for a specific path, from a specific project cache, or from the 353 * framework cache. 354 * @param value the path of the bitmap 355 * @param projectKey the key of the project, or null to query the framework cache. 356 * @return the cached Bitmap or null if not found. 357 */ 358 public static Bitmap getCachedBitmap(String value, Object projectKey) { 359 if (projectKey != null) { 360 Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey); 361 if (map != null) { 362 SoftReference<Bitmap> ref = map.get(value); 363 if (ref != null) { 364 return ref.get(); 365 } 366 } 367 } else { 368 SoftReference<Bitmap> ref = sFrameworkBitmapCache.get(value); 369 if (ref != null) { 370 return ref.get(); 371 } 372 } 373 374 return null; 375 } 376 377 /** 378 * Sets a bitmap in a project cache or in the framework cache. 379 * @param value the path of the bitmap 380 * @param bmp the Bitmap object 381 * @param projectKey the key of the project, or null to put the bitmap in the framework cache. 382 */ 383 public static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) { 384 if (projectKey != null) { 385 Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey); 386 387 if (map == null) { 388 map = new HashMap<String, SoftReference<Bitmap>>(); 389 sProjectBitmapCache.put(projectKey, map); 390 } 391 392 map.put(value, new SoftReference<Bitmap>(bmp)); 393 } else { 394 sFrameworkBitmapCache.put(value, new SoftReference<Bitmap>(bmp)); 395 } 396 } 397 398 /** 399 * Returns the 9 patch for a specific path, from a specific project cache, or from the 400 * framework cache. 401 * @param value the path of the 9 patch 402 * @param projectKey the key of the project, or null to query the framework cache. 403 * @return the cached 9 patch or null if not found. 404 */ 405 public static NinePatch getCached9Patch(String value, Object projectKey) { 406 if (projectKey != null) { 407 Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey); 408 409 if (map != null) { 410 SoftReference<NinePatch> ref = map.get(value); 411 if (ref != null) { 412 return ref.get(); 413 } 414 } 415 } else { 416 SoftReference<NinePatch> ref = sFramework9PatchCache.get(value); 417 if (ref != null) { 418 return ref.get(); 419 } 420 } 421 422 return null; 423 } 424} 425