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