Bridge.java revision d24b8183b93e781080b2c16c487e60d51c12da31
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.internal.util.XmlUtils; 20import com.android.layoutlib.api.ILayoutBridge; 21import com.android.layoutlib.api.ILayoutLog; 22import com.android.layoutlib.api.ILayoutResult; 23import com.android.layoutlib.api.IProjectCallback; 24import com.android.layoutlib.api.IResourceValue; 25import com.android.layoutlib.api.IStyleResourceValue; 26import com.android.layoutlib.api.IXmlPullParser; 27import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; 28import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo; 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.Rect; 35import android.graphics.Region; 36import android.graphics.Typeface; 37import android.graphics.drawable.Drawable; 38import android.os.Handler; 39import android.os.IBinder; 40import android.os.Looper; 41import android.os.ParcelFileDescriptor; 42import android.os.RemoteException; 43import android.util.TypedValue; 44import android.view.BridgeInflater; 45import android.view.IWindow; 46import android.view.IWindowSession; 47import android.view.KeyEvent; 48import android.view.MotionEvent; 49import android.view.Surface; 50import android.view.SurfaceView; 51import android.view.View; 52import android.view.ViewGroup; 53import android.view.View.AttachInfo; 54import android.view.View.MeasureSpec; 55import android.view.WindowManager.LayoutParams; 56import android.widget.FrameLayout; 57 58import java.lang.reflect.Field; 59import java.lang.reflect.Modifier; 60import java.util.Collection; 61import java.util.HashMap; 62import java.util.Map; 63 64/** 65 * Main entry point of the LayoutLib Bridge. 66 * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call 67 * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}. 68 */ 69public final class Bridge implements ILayoutBridge { 70 71 private static final int DEFAULT_TITLE_BAR_HEIGHT = 25; 72 private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; 73 74 public static class StaticMethodNotImplementedException extends RuntimeException { 75 public StaticMethodNotImplementedException(String msg) { 76 super(msg); 77 } 78 } 79 80 /** 81 * Maps from id to resource name/type. 82 */ 83 private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>(); 84 /** 85 * Same as sRMap except for int[] instead of int resources. 86 */ 87 private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>(); 88 /** 89 * Reverse map compared to sRMap, resource type -> (resource name -> id) 90 */ 91 private final static Map<String, Map<String, Integer>> sRFullMap = 92 new HashMap<String, Map<String,Integer>>(); 93 94 private final static Map<Object, Map<String, Bitmap>> sProjectBitmapCache = 95 new HashMap<Object, Map<String, Bitmap>>(); 96 private final static Map<Object, Map<String, NinePatch>> sProject9PatchCache = 97 new HashMap<Object, Map<String, NinePatch>>(); 98 99 private final static Map<String, Bitmap> sFrameworkBitmapCache = new HashMap<String, Bitmap>(); 100 private final static Map<String, NinePatch> sFramework9PatchCache = 101 new HashMap<String, NinePatch>(); 102 103 private static Map<String, Map<String, Integer>> sEnumValueMap; 104 105 /** 106 * A default logger than prints to stdout/stderr. 107 */ 108 private final static ILayoutLog sDefaultLogger = new ILayoutLog() { 109 public void error(String message) { 110 System.err.println(message); 111 } 112 113 public void error(Throwable t) { 114 String message = t.getMessage(); 115 if (message == null) { 116 message = t.getClass().getName(); 117 } 118 119 System.err.println(message); 120 } 121 122 public void warning(String message) { 123 System.out.println(message); 124 } 125 }; 126 127 /** 128 * Logger defined during a compute layout operation. 129 * <p/> 130 * This logger is generally set to {@link #sDefaultLogger} except during rendering 131 * operations when it might be set to a specific provided logger. 132 * <p/> 133 * To change this value, use a block synchronized on {@link #sDefaultLogger}. 134 */ 135 private static ILayoutLog sLogger = sDefaultLogger; 136 137 /* 138 * (non-Javadoc) 139 * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel() 140 */ 141 public int getApiLevel() { 142 return API_CURRENT; 143 } 144 145 /* 146 * (non-Javadoc) 147 * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map) 148 */ 149 public boolean init( 150 String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { 151 152 return sinit(fontOsLocation, enumValueMap); 153 } 154 155 private static synchronized boolean sinit(String fontOsLocation, 156 Map<String, Map<String, Integer>> enumValueMap) { 157 158 // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener 159 // on static (native) methods which prints the signature on the console and 160 // throws an exception. 161 // This is useful when testing the rendering in ADT to identify static native 162 // methods that are ignored -- layoutlib_create makes them returns 0/false/null 163 // which is generally OK yet might be a problem, so this is how you'd find out. 164 // 165 // Currently layoutlib_create only overrides static native method. 166 // Static non-natives are not overridden and thus do not get here. 167 final String debug = System.getenv("DEBUG_LAYOUT"); 168 if (debug != null && !debug.equals("0") && !debug.equals("false")) { 169 170 OverrideMethod.setDefaultListener(new MethodAdapter() { 171 @Override 172 public void onInvokeV(String signature, boolean isNative, Object caller) { 173 if (sLogger != null) { 174 synchronized (sDefaultLogger) { 175 sLogger.error("Missing Stub: " + signature + 176 (isNative ? " (native)" : "")); 177 } 178 } 179 180 if (debug.equalsIgnoreCase("throw")) { 181 // Throwing this exception doesn't seem that useful. It breaks 182 // the layout editor yet doesn't display anything meaningful to the 183 // user. Having the error in the console is just as useful. We'll 184 // throw it only if the environment variable is "throw" or "THROW". 185 throw new StaticMethodNotImplementedException(signature); 186 } 187 } 188 }); 189 } 190 191 // Override View.isInEditMode to return true. 192 // 193 // This allows custom views that are drawn in the Graphical Layout Editor to adapt their 194 // rendering for preview. Most important this let custom views know that they can't expect 195 // the rest of their activities to be alive. 196 OverrideMethod.setMethodListener("android.view.View#isInEditMode()Z", 197 new MethodAdapter() { 198 @Override 199 public int onInvokeI(String signature, boolean isNative, Object caller) { 200 return 1; 201 } 202 } 203 ); 204 205 // load the fonts. 206 FontLoader fontLoader = FontLoader.create(fontOsLocation); 207 if (fontLoader != null) { 208 Typeface.init(fontLoader); 209 } else { 210 return false; 211 } 212 213 sEnumValueMap = enumValueMap; 214 215 // now parse com.android.internal.R (and only this one as android.R is a subset of 216 // the internal version), and put the content in the maps. 217 try { 218 // WARNING: this only works because the class is already loaded, and therefore 219 // the objects returned by Field.get() are the same as the ones used by 220 // the code accessing the R class. 221 // int[] does not implement equals/hashCode, and if the parsing used a different class 222 // loader for the R class, this would NOT work. 223 Class<?> r = com.android.internal.R.class; 224 225 for (Class<?> inner : r.getDeclaredClasses()) { 226 String resType = inner.getSimpleName(); 227 228 Map<String, Integer> fullMap = new HashMap<String, Integer>(); 229 sRFullMap.put(resType, fullMap); 230 231 for (Field f : inner.getDeclaredFields()) { 232 // only process static final fields. Since the final attribute may have 233 // been altered by layoutlib_create, we only check static 234 int modifiers = f.getModifiers(); 235 if (Modifier.isStatic(modifiers)) { 236 Class<?> type = f.getType(); 237 if (type.isArray() && type.getComponentType() == int.class) { 238 // if the object is an int[] we put it in sRArrayMap 239 sRArrayMap.put((int[]) f.get(null), f.getName()); 240 } else if (type == int.class) { 241 Integer value = (Integer) f.get(null); 242 sRMap.put(value, new String[] { f.getName(), resType }); 243 fullMap.put(f.getName(), value); 244 } else { 245 assert false; 246 } 247 } 248 } 249 } 250 } catch (IllegalArgumentException e) { 251 // FIXME: log/return the error (there's no logger object at this point!) 252 e.printStackTrace(); 253 return false; 254 } catch (IllegalAccessException e) { 255 e.printStackTrace(); 256 return false; 257 } 258 259 return true; 260 } 261 262 /* 263 * For compatilibty purposes, we implement the old deprecated version of computeLayout. 264 * (non-Javadoc) 265 * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) 266 */ 267 @Deprecated 268 public ILayoutResult computeLayout(IXmlPullParser layoutDescription, 269 Object projectKey, 270 int screenWidth, int screenHeight, String themeName, 271 Map<String, Map<String, IResourceValue>> projectResources, 272 Map<String, Map<String, IResourceValue>> frameworkResources, 273 IProjectCallback customViewLoader, ILayoutLog logger) { 274 boolean isProjectTheme = false; 275 if (themeName.charAt(0) == '*') { 276 themeName = themeName.substring(1); 277 isProjectTheme = true; 278 } 279 280 return computeLayout(layoutDescription, projectKey, screenWidth, screenHeight, themeName, isProjectTheme, 281 projectResources, frameworkResources, customViewLoader, logger); 282 } 283 284 /* 285 * (non-Javadoc) 286 * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) 287 */ 288 public ILayoutResult computeLayout(IXmlPullParser layoutDescription, 289 Object projectKey, 290 int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, 291 Map<String, Map<String, IResourceValue>> projectResources, 292 Map<String, Map<String, IResourceValue>> frameworkResources, 293 IProjectCallback customViewLoader, ILayoutLog logger) { 294 if (logger == null) { 295 logger = sDefaultLogger; 296 } 297 298 synchronized (sDefaultLogger) { 299 sLogger = logger; 300 } 301 302 // find the current theme and compute the style inheritance map 303 Map<IStyleResourceValue, IStyleResourceValue> styleParentMap = 304 new HashMap<IStyleResourceValue, IStyleResourceValue>(); 305 306 IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme, 307 projectResources.get(BridgeConstants.RES_STYLE), 308 frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap); 309 310 BridgeContext context = null; 311 try { 312 context = new BridgeContext(projectKey, currentTheme, projectResources, 313 frameworkResources, styleParentMap, customViewLoader, logger); 314 BridgeInflater inflater = new BridgeInflater(context, customViewLoader); 315 context.setBridgeInflater(inflater); 316 317 IResourceValue windowBackground = null; 318 int screenOffset = 0; 319 if (currentTheme != null) { 320 windowBackground = context.findItemInStyle(currentTheme, "windowBackground"); 321 windowBackground = context.resolveResValue(windowBackground); 322 323 screenOffset = getScreenOffset(currentTheme, context); 324 } 325 326 // we need to make sure the Looper has been initialized for this thread. 327 // this is required for View that creates Handler objects. 328 if (Looper.myLooper() == null) { 329 Looper.prepare(); 330 } 331 332 BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription, 333 context, false /* platformResourceFlag */); 334 335 ViewGroup root = new FrameLayout(context); 336 337 View view = inflater.inflate(parser, root); 338 339 // set the AttachInfo on the root view. 340 AttachInfo info = new AttachInfo(new WindowSession(), new Window(), 341 new Handler(), null); 342 info.mHasWindowFocus = true; 343 info.mWindowVisibility = View.VISIBLE; 344 info.mInTouchMode = false; // this is so that we can display selections. 345 root.dispatchAttachedToWindow(info, 0); 346 347 // get the background drawable 348 if (windowBackground != null) { 349 Drawable d = ResourceHelper.getDrawable(windowBackground.getValue(), 350 context, true /* isFramework */); 351 root.setBackgroundDrawable(d); 352 } 353 354 int w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY); 355 int h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset, 356 MeasureSpec.EXACTLY); 357 358 // measure the views 359 view.measure(w_spec, h_spec); 360 view.layout(0, screenOffset, screenWidth, screenHeight); 361 362 // draw them 363 BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset, 364 logger); 365 366 root.draw(canvas); 367 canvas.dispose(); 368 369 return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context), 370 canvas.getImage()); 371 } catch (Throwable e) { 372 // get the real cause of the exception. 373 Throwable t = e; 374 while (t.getCause() != null) { 375 t = t.getCause(); 376 } 377 378 // log it 379 logger.error(t); 380 381 // then return with an ERROR status and the message from the real exception 382 return new LayoutResult(ILayoutResult.ERROR, 383 t.getClass().getSimpleName() + ": " + t.getMessage()); 384 } finally { 385 // Make sure to remove static references, otherwise we could not unload the lib 386 BridgeResources.clearSystem(); 387 BridgeAssetManager.clearSystem(); 388 389 // Remove the global logger 390 synchronized (sDefaultLogger) { 391 sLogger = sDefaultLogger; 392 } 393 } 394 } 395 396 /* 397 * (non-Javadoc) 398 * @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object) 399 */ 400 public void clearCaches(Object projectKey) { 401 if (projectKey != null) { 402 sProjectBitmapCache.remove(projectKey); 403 sProject9PatchCache.remove(projectKey); 404 } 405 } 406 407 /** 408 * Returns details of a framework resource from its integer value. 409 * @param value the integer value 410 * @return an array of 2 strings containing the resource name and type, or null if the id 411 * does not match any resource. 412 */ 413 public static String[] resolveResourceValue(int value) { 414 return sRMap.get(value); 415 416 } 417 418 /** 419 * Returns the name of a framework resource whose value is an int array. 420 * @param array 421 */ 422 public static String resolveResourceValue(int[] array) { 423 return sRArrayMap.get(array); 424 } 425 426 /** 427 * Returns the integer id of a framework resource, from a given resource type and resource name. 428 * @param type the type of the resource 429 * @param name the name of the resource. 430 * @return an {@link Integer} containing the resource id, or null if no resource were found. 431 */ 432 public static Integer getResourceValue(String type, String name) { 433 Map<String, Integer> map = sRFullMap.get(type); 434 if (map != null) { 435 return map.get(name); 436 } 437 438 return null; 439 } 440 441 static Map<String, Integer> getEnumValues(String attributeName) { 442 if (sEnumValueMap != null) { 443 return sEnumValueMap.get(attributeName); 444 } 445 446 return null; 447 } 448 449 /** 450 * Visits a View and its children and generate a {@link ILayoutViewInfo} containing the 451 * bounds of all the views. 452 * @param view the root View 453 * @param context the context. 454 */ 455 private ILayoutViewInfo visit(View view, BridgeContext context) { 456 if (view == null) { 457 return null; 458 } 459 460 LayoutViewInfo result = new LayoutViewInfo(view.getClass().getName(), 461 context.getViewKey(view), 462 view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); 463 464 if (view instanceof ViewGroup) { 465 ViewGroup group = ((ViewGroup) view); 466 int n = group.getChildCount(); 467 ILayoutViewInfo[] children = new ILayoutViewInfo[n]; 468 for (int i = 0; i < group.getChildCount(); i++) { 469 children[i] = visit(group.getChildAt(i), context); 470 } 471 result.setChildren(children); 472 } 473 474 return result; 475 } 476 477 /** 478 * Compute style information from the given list of style for the project and framework. 479 * @param themeName the name of the current theme. In order to differentiate project and 480 * platform themes sharing the same name, all project themes must be prepended with 481 * a '*' character. 482 * @param isProjectTheme Is this a project theme 483 * @param inProjectStyleMap the project style map 484 * @param inFrameworkStyleMap the framework style map 485 * @param outInheritanceMap the map of style inheritance. This is filled by the method 486 * @return the {@link IStyleResourceValue} matching <var>themeName</var> 487 */ 488 private IStyleResourceValue computeStyleMaps( 489 String themeName, boolean isProjectTheme, Map<String, 490 IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap, 491 Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { 492 493 if (inProjectStyleMap != null && inFrameworkStyleMap != null) { 494 // first, get the theme 495 IResourceValue theme = null; 496 497 // project theme names have been prepended with a * 498 if (isProjectTheme) { 499 theme = inProjectStyleMap.get(themeName); 500 } else { 501 theme = inFrameworkStyleMap.get(themeName); 502 } 503 504 if (theme instanceof IStyleResourceValue) { 505 // compute the inheritance map for both the project and framework styles 506 computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap, 507 inFrameworkStyleMap, outInheritanceMap); 508 509 // Compute the style inheritance for the framework styles/themes. 510 // Since, for those, the style parent values do not contain 'android:' 511 // we want to force looking in the framework style only to avoid using 512 // similarly named styles from the project. 513 // To do this, we pass null in lieu of the project style map. 514 computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */, 515 inFrameworkStyleMap, outInheritanceMap); 516 517 return (IStyleResourceValue)theme; 518 } 519 } 520 521 return null; 522 } 523 524 /** 525 * Compute the parent style for all the styles in a given list. 526 * @param styles the styles for which we compute the parent. 527 * @param inProjectStyleMap the map of project styles. 528 * @param inFrameworkStyleMap the map of framework styles. 529 * @param outInheritanceMap the map of style inheritance. This is filled by the method. 530 */ 531 private void computeStyleInheritance(Collection<IResourceValue> styles, 532 Map<String, IResourceValue> inProjectStyleMap, 533 Map<String, IResourceValue> inFrameworkStyleMap, 534 Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { 535 for (IResourceValue value : styles) { 536 if (value instanceof IStyleResourceValue) { 537 IStyleResourceValue style = (IStyleResourceValue)value; 538 IStyleResourceValue parentStyle = null; 539 540 // first look for a specified parent. 541 String parentName = style.getParentStyle(); 542 543 // no specified parent? try to infer it from the name of the style. 544 if (parentName == null) { 545 parentName = getParentName(value.getName()); 546 } 547 548 if (parentName != null) { 549 parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap); 550 551 if (parentStyle != null) { 552 outInheritanceMap.put(style, parentStyle); 553 } 554 } 555 } 556 } 557 } 558 559 /** 560 * Searches for and returns the {@link IStyleResourceValue} from a given name. 561 * <p/>The format of the name can be: 562 * <ul> 563 * <li>[android:]<name></li> 564 * <li>[android:]style/<name></li> 565 * <li>@[android:]style/<name></li> 566 * </ul> 567 * @param parentName the name of the style. 568 * @param inProjectStyleMap the project style map. Can be <code>null</code> 569 * @param inFrameworkStyleMap the framework style map. 570 * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found. 571 */ 572 private IStyleResourceValue getStyle(String parentName, 573 Map<String, IResourceValue> inProjectStyleMap, 574 Map<String, IResourceValue> inFrameworkStyleMap) { 575 boolean frameworkOnly = false; 576 577 String name = parentName; 578 579 // remove the useless @ if it's there 580 if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) { 581 name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length()); 582 } 583 584 // check for framework identifier. 585 if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) { 586 frameworkOnly = true; 587 name = name.substring(BridgeConstants.PREFIX_ANDROID.length()); 588 } 589 590 // at this point we could have the format style/<name>. we want only the name 591 if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) { 592 name = name.substring(BridgeConstants.REFERENCE_STYLE.length()); 593 } 594 595 IResourceValue parent = null; 596 597 // if allowed, search in the project resources. 598 if (frameworkOnly == false && inProjectStyleMap != null) { 599 parent = inProjectStyleMap.get(name); 600 } 601 602 // if not found, then look in the framework resources. 603 if (parent == null) { 604 parent = inFrameworkStyleMap.get(name); 605 } 606 607 // make sure the result is the proper class type and return it. 608 if (parent instanceof IStyleResourceValue) { 609 return (IStyleResourceValue)parent; 610 } 611 612 sLogger.error(String.format("Unable to resolve parent style name: ", parentName)); 613 614 return null; 615 } 616 617 /** 618 * Computes the name of the parent style, or <code>null</code> if the style is a root style. 619 */ 620 private String getParentName(String styleName) { 621 int index = styleName.lastIndexOf('.'); 622 if (index != -1) { 623 return styleName.substring(0, index); 624 } 625 626 return null; 627 } 628 629 /** 630 * Returns the top screen offset. This depends on whether the current theme defines the user 631 * of the title and status bars. 632 * @return the pixel height offset 633 */ 634 private int getScreenOffset(IStyleResourceValue currentTheme, BridgeContext context) { 635 int offset = 0; 636 637 // get the title bar flag from the current theme. 638 IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle"); 639 640 // because it may reference something else, we resolve it. 641 value = context.resolveResValue(value); 642 643 // if there's a value and it's true (default is false) 644 if (value == null || value.getValue() == null || 645 XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { 646 // get value from the theme. 647 value = context.findItemInStyle(currentTheme, "windowTitleSize"); 648 649 // resolve it 650 value = context.resolveResValue(value); 651 652 // default value 653 offset = DEFAULT_TITLE_BAR_HEIGHT; 654 655 // get the real value; 656 if (value != null) { 657 TypedValue typedValue = ResourceHelper.getValue(value.getValue()); 658 if (typedValue != null) { 659 offset = (int)typedValue.getDimension(context.getResources().mMetrics); 660 } 661 } 662 } 663 664 // get the fullscreen flag from the current theme. 665 value = context.findItemInStyle(currentTheme, "windowFullscreen"); 666 667 // because it may reference something else, we resolve it. 668 value = context.resolveResValue(value); 669 670 if (value == null || value.getValue() == null || 671 XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { 672 // FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it. 673 offset += DEFAULT_STATUS_BAR_HEIGHT; 674 } 675 676 return offset; 677 } 678 679 /** 680 * Returns the bitmap for a specific path, from a specific project cache, or from the 681 * framework cache. 682 * @param value the path of the bitmap 683 * @param projectKey the key of the project, or null to query the framework cache. 684 * @return the cached Bitmap or null if not found. 685 */ 686 static Bitmap getCachedBitmap(String value, Object projectKey) { 687 if (projectKey != null) { 688 Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey); 689 if (map != null) { 690 return map.get(value); 691 } 692 693 return null; 694 } 695 696 return sFrameworkBitmapCache.get(value); 697 } 698 699 /** 700 * Sets a bitmap in a project cache or in the framework cache. 701 * @param value the path of the bitmap 702 * @param bmp the Bitmap object 703 * @param projectKey the key of the project, or null to put the bitmap in the framework cache. 704 */ 705 static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) { 706 if (projectKey != null) { 707 Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey); 708 709 if (map == null) { 710 map = new HashMap<String, Bitmap>(); 711 sProjectBitmapCache.put(projectKey, map); 712 } 713 714 map.put(value, bmp); 715 } 716 717 sFrameworkBitmapCache.put(value, bmp); 718 } 719 720 /** 721 * Returns the 9 patch for a specific path, from a specific project cache, or from the 722 * framework cache. 723 * @param value the path of the 9 patch 724 * @param projectKey the key of the project, or null to query the framework cache. 725 * @return the cached 9 patch or null if not found. 726 */ 727 static NinePatch getCached9Patch(String value, Object projectKey) { 728 if (projectKey != null) { 729 Map<String, NinePatch> map = sProject9PatchCache.get(projectKey); 730 731 if (map != null) { 732 return map.get(value); 733 } 734 735 return null; 736 } 737 738 return sFramework9PatchCache.get(value); 739 } 740 741 /** 742 * Sets a 9 patch in a project cache or in the framework cache. 743 * @param value the path of the 9 patch 744 * @param ninePatch the 9 patch object 745 * @param projectKey the key of the project, or null to put the bitmap in the framework cache. 746 */ 747 static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) { 748 if (projectKey != null) { 749 Map<String, NinePatch> map = sProject9PatchCache.get(projectKey); 750 751 if (map == null) { 752 map = new HashMap<String, NinePatch>(); 753 sProject9PatchCache.put(projectKey, map); 754 } 755 756 map.put(value, ninePatch); 757 } 758 759 sFramework9PatchCache.put(value, ninePatch); 760 } 761 762 /** 763 * Implementation of {@link IWindowSession} so that mSession is not null in 764 * the {@link SurfaceView}. 765 */ 766 private static final class WindowSession implements IWindowSession { 767 768 @SuppressWarnings("unused") 769 public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3) 770 throws RemoteException { 771 // pass for now. 772 return 0; 773 } 774 775 @SuppressWarnings("unused") 776 public void finishDrawing(IWindow arg0) throws RemoteException { 777 // pass for now. 778 } 779 780 @SuppressWarnings("unused") 781 public void finishKey(IWindow arg0) throws RemoteException { 782 // pass for now. 783 } 784 785 @SuppressWarnings("unused") 786 public boolean getInTouchMode() throws RemoteException { 787 // pass for now. 788 return false; 789 } 790 791 @SuppressWarnings("unused") 792 public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { 793 // pass for now. 794 return null; 795 } 796 797 @SuppressWarnings("unused") 798 public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException { 799 // pass for now. 800 return null; 801 } 802 803 @SuppressWarnings("unused") 804 public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4, 805 boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Surface arg8) 806 throws RemoteException { 807 // pass for now. 808 return 0; 809 } 810 811 public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { 812 // pass for now. 813 } 814 815 @SuppressWarnings("unused") 816 public void remove(IWindow arg0) throws RemoteException { 817 // pass for now. 818 } 819 820 @SuppressWarnings("unused") 821 public void setInTouchMode(boolean arg0) throws RemoteException { 822 // pass for now. 823 } 824 825 @SuppressWarnings("unused") 826 public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException { 827 // pass for now. 828 } 829 830 public void setInsets(IWindow window, int touchable, Rect contentInsets, 831 Rect visibleInsets) { 832 // pass for now. 833 } 834 835 public IBinder asBinder() { 836 // pass for now. 837 return null; 838 } 839 } 840 841 /** 842 * Implementation of {@link IWindow} to pass to the {@link AttachInfo}. 843 */ 844 private static final class Window implements IWindow { 845 846 @SuppressWarnings("unused") 847 public void dispatchAppVisibility(boolean arg0) throws RemoteException { 848 // pass for now. 849 } 850 851 @SuppressWarnings("unused") 852 public void dispatchGetNewSurface() throws RemoteException { 853 // pass for now. 854 } 855 856 @SuppressWarnings("unused") 857 public void dispatchKey(KeyEvent arg0) throws RemoteException { 858 // pass for now. 859 } 860 861 @SuppressWarnings("unused") 862 public void dispatchPointer(MotionEvent arg0, long arg1) throws RemoteException { 863 // pass for now. 864 } 865 866 @SuppressWarnings("unused") 867 public void dispatchTrackball(MotionEvent arg0, long arg1) throws RemoteException { 868 // pass for now. 869 } 870 871 @SuppressWarnings("unused") 872 public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2) 873 throws RemoteException { 874 // pass for now. 875 } 876 877 @SuppressWarnings("unused") 878 public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4) 879 throws RemoteException { 880 // pass for now. 881 } 882 883 @SuppressWarnings("unused") 884 public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException { 885 // pass for now. 886 } 887 888 public IBinder asBinder() { 889 // pass for now. 890 return null; 891 } 892 } 893 894} 895