1 /* 2 * Copyright (C) 2010 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 17 package com.android.ide.common.rendering; 18 19 import static com.android.ide.common.rendering.api.Result.Status.ERROR_REFLECTION; 20 21 import com.android.ide.common.log.ILogger; 22 import com.android.ide.common.rendering.api.Bridge; 23 import com.android.ide.common.rendering.api.Capability; 24 import com.android.ide.common.rendering.api.DrawableParams; 25 import com.android.ide.common.rendering.api.ILayoutPullParser; 26 import com.android.ide.common.rendering.api.LayoutLog; 27 import com.android.ide.common.rendering.api.RenderSession; 28 import com.android.ide.common.rendering.api.ResourceValue; 29 import com.android.ide.common.rendering.api.Result; 30 import com.android.ide.common.rendering.api.Result.Status; 31 import com.android.ide.common.rendering.api.SessionParams; 32 import com.android.ide.common.rendering.api.SessionParams.RenderingMode; 33 import com.android.ide.common.rendering.api.ViewInfo; 34 import com.android.ide.common.rendering.legacy.ILegacyPullParser; 35 import com.android.ide.common.rendering.legacy.LegacyCallback; 36 import com.android.ide.common.resources.ResourceResolver; 37 import com.android.ide.common.sdk.LoadStatus; 38 import com.android.layoutlib.api.ILayoutBridge; 39 import com.android.layoutlib.api.ILayoutLog; 40 import com.android.layoutlib.api.ILayoutResult; 41 import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; 42 import com.android.layoutlib.api.IProjectCallback; 43 import com.android.layoutlib.api.IResourceValue; 44 import com.android.layoutlib.api.IXmlPullParser; 45 import com.android.resources.ResourceType; 46 47 import java.awt.image.BufferedImage; 48 import java.io.File; 49 import java.lang.reflect.Constructor; 50 import java.lang.reflect.Field; 51 import java.lang.reflect.Method; 52 import java.net.URI; 53 import java.net.URL; 54 import java.net.URLClassLoader; 55 import java.util.ArrayList; 56 import java.util.HashMap; 57 import java.util.List; 58 import java.util.Map; 59 import java.util.Map.Entry; 60 61 /** 62 * Class to use the Layout library. 63 * <p/> 64 * Use {@link #load(String, ILogger)} to load the jar file. 65 * <p/> 66 * Use the layout library with: 67 * {@link #init(String, Map)}, {@link #supports(Capability)}, {@link #createSession(SessionParams)}, 68 * {@link #dispose()}, {@link #clearCaches(Object)}. 69 * 70 * <p/> 71 * For client wanting to access both new and old (pre API level 5) layout libraries, it is 72 * important that the following interfaces be used:<br> 73 * {@link ILegacyPullParser} instead of {@link ILayoutPullParser}<br> 74 * {@link LegacyCallback} instead of {@link com.android.ide.common.rendering.api.IProjectCallback}. 75 * <p/> 76 * These interfaces will ensure that both new and older Layout libraries can be accessed. 77 */ 78 @SuppressWarnings("deprecation") 79 public class LayoutLibrary { 80 81 public final static String CLASS_BRIDGE = "com.android.layoutlib.bridge.Bridge"; //$NON-NLS-1$ 82 83 /** Link to the layout bridge */ 84 private final Bridge mBridge; 85 /** Link to a ILayoutBridge in case loaded an older library */ 86 private final ILayoutBridge mLegacyBridge; 87 /** Status of the layoutlib.jar loading */ 88 private final LoadStatus mStatus; 89 /** Message associated with the {@link LoadStatus}. This is mostly used when 90 * {@link #getStatus()} returns {@link LoadStatus#FAILED}. 91 */ 92 private final String mLoadMessage; 93 /** classloader used to load the jar file */ 94 private final ClassLoader mClassLoader; 95 96 // Reflection data for older Layout Libraries. 97 private Method mViewGetParentMethod; 98 private Method mViewGetBaselineMethod; 99 private Method mViewParentIndexOfChildMethod; 100 private Class<?> mMarginLayoutParamClass; 101 private Field mLeftMarginField; 102 private Field mTopMarginField; 103 private Field mRightMarginField; 104 private Field mBottomMarginField; 105 106 /** 107 * Returns the {@link LoadStatus} of the loading of the layoutlib jar file. 108 */ 109 public LoadStatus getStatus() { 110 return mStatus; 111 } 112 113 /** Returns the message associated with the {@link LoadStatus}. This is mostly used when 114 * {@link #getStatus()} returns {@link LoadStatus#FAILED}. 115 */ 116 public String getLoadMessage() { 117 return mLoadMessage; 118 } 119 120 /** 121 * Returns the classloader used to load the classes in the layoutlib jar file. 122 */ 123 public ClassLoader getClassLoader() { 124 return mClassLoader; 125 } 126 127 /** 128 * Loads the layoutlib.jar file located at the given path and returns a {@link LayoutLibrary} 129 * object representing the result. 130 * <p/> 131 * If loading failed {@link #getStatus()} will reflect this, and {@link #getBridge()} will 132 * return null. 133 * 134 * @param layoutLibJarOsPath the path of the jar file 135 * @param log an optional log file. 136 * @return a {@link LayoutLibrary} object always. 137 */ 138 public static LayoutLibrary load(String layoutLibJarOsPath, ILogger log, String toolName) { 139 140 LoadStatus status = LoadStatus.LOADING; 141 String message = null; 142 Bridge bridge = null; 143 ILayoutBridge legacyBridge = null; 144 ClassLoader classLoader = null; 145 146 try { 147 // get the URL for the file. 148 File f = new File(layoutLibJarOsPath); 149 if (f.isFile() == false) { 150 if (log != null) { 151 log.error(null, "layoutlib.jar is missing!"); //$NON-NLS-1$ 152 } 153 } else { 154 URI uri = f.toURI(); 155 URL url = uri.toURL(); 156 157 // create a class loader. Because this jar reference interfaces 158 // that are in the editors plugin, it's important to provide 159 // a parent class loader. 160 classLoader = new URLClassLoader( 161 new URL[] { url }, 162 LayoutLibrary.class.getClassLoader()); 163 164 // load the class 165 Class<?> clazz = classLoader.loadClass(CLASS_BRIDGE); 166 if (clazz != null) { 167 // instantiate an object of the class. 168 Constructor<?> constructor = clazz.getConstructor(); 169 if (constructor != null) { 170 Object bridgeObject = constructor.newInstance(); 171 if (bridgeObject instanceof Bridge) { 172 bridge = (Bridge)bridgeObject; 173 } else if (bridgeObject instanceof ILayoutBridge) { 174 legacyBridge = (ILayoutBridge) bridgeObject; 175 } 176 } 177 } 178 179 if (bridge == null && legacyBridge == null) { 180 status = LoadStatus.FAILED; 181 message = "Failed to load " + CLASS_BRIDGE; //$NON-NLS-1$ 182 if (log != null) { 183 log.error(null, 184 "Failed to load " + //$NON-NLS-1$ 185 CLASS_BRIDGE + 186 " from " + //$NON-NLS-1$ 187 layoutLibJarOsPath); 188 } 189 } else { 190 // mark the lib as loaded, unless it's overridden below. 191 status = LoadStatus.LOADED; 192 193 // check the API, only if it's not a legacy bridge 194 if (bridge != null) { 195 int api = bridge.getApiLevel(); 196 if (api > Bridge.API_CURRENT) { 197 status = LoadStatus.FAILED; 198 message = String.format( 199 "This version of the rendering library is more recent than your version of %1$s. Please update %1$s", toolName); 200 } 201 } 202 } 203 } 204 } catch (Throwable t) { 205 status = LoadStatus.FAILED; 206 Throwable cause = t; 207 while (cause.getCause() != null) { 208 cause = cause.getCause(); 209 } 210 message = "Failed to load the LayoutLib: " + cause.getMessage(); 211 // log the error. 212 if (log != null) { 213 log.error(t, message); 214 } 215 } 216 217 return new LayoutLibrary(bridge, legacyBridge, classLoader, status, message); 218 } 219 220 // ------ Layout Lib API proxy 221 222 /** 223 * Returns the API level of the layout library. 224 */ 225 public int getApiLevel() { 226 if (mBridge != null) { 227 return mBridge.getApiLevel(); 228 } 229 230 if (mLegacyBridge != null) { 231 return getLegacyApiLevel(); 232 } 233 234 return 0; 235 } 236 237 /** 238 * Returns the revision of the library inside a given (layoutlib) API level. 239 * The true version number of the library is {@link #getApiLevel()}.{@link #getRevision()} 240 */ 241 public int getRevision() { 242 if (mBridge != null) { 243 return mBridge.getRevision(); 244 } 245 246 return 0; 247 } 248 249 /** 250 * Returns whether the LayoutLibrary supports a given {@link Capability}. 251 * @return true if it supports it. 252 * 253 * @see Bridge#getCapabilities() 254 * 255 */ 256 public boolean supports(Capability capability) { 257 if (mBridge != null) { 258 return mBridge.getCapabilities().contains(capability); 259 } 260 261 if (mLegacyBridge != null) { 262 switch (capability) { 263 case UNBOUND_RENDERING: 264 // legacy stops at 4. 5 is new API. 265 return getLegacyApiLevel() == 4; 266 } 267 } 268 269 return false; 270 } 271 272 /** 273 * Initializes the Layout Library object. This must be called before any other action is taken 274 * on the instance. 275 * 276 * @param platformProperties The build properties for the platform. 277 * @param fontLocation the location of the fonts in the SDK target. 278 * @param enumValueMap map attrName => { map enumFlagName => Integer value }. This is typically 279 * read from attrs.xml in the SDK target. 280 * @param log a {@link LayoutLog} object. Can be null. 281 * @return true if success. 282 * 283 * @see Bridge#init(String, Map) 284 */ 285 public boolean init(Map<String, String> platformProperties, 286 File fontLocation, 287 Map<String, Map<String, Integer>> enumValueMap, 288 LayoutLog log) { 289 if (mBridge != null) { 290 return mBridge.init(platformProperties, fontLocation, enumValueMap, log); 291 } else if (mLegacyBridge != null) { 292 return mLegacyBridge.init(fontLocation.getAbsolutePath(), enumValueMap); 293 } 294 295 return false; 296 } 297 298 /** 299 * Prepares the layoutlib to unloaded. 300 * 301 * @see Bridge#dispose() 302 */ 303 public boolean dispose() { 304 if (mBridge != null) { 305 return mBridge.dispose(); 306 } 307 308 return true; 309 } 310 311 /** 312 * Starts a layout session by inflating and rendering it. The method returns a 313 * {@link RenderSession} on which further actions can be taken. 314 * <p/> 315 * Before taking further actions on the scene, it is recommended to use 316 * {@link #supports(Capability)} to check what the scene can do. 317 * 318 * @return a new {@link ILayoutScene} object that contains the result of the scene creation and 319 * first rendering or null if {@link #getStatus()} doesn't return {@link LoadStatus#LOADED}. 320 * 321 * @see Bridge#createSession(SessionParams) 322 */ 323 public RenderSession createSession(SessionParams params) { 324 if (mBridge != null) { 325 RenderSession session = mBridge.createSession(params); 326 if (params.getExtendedViewInfoMode() && 327 mBridge.getCapabilities().contains(Capability.EXTENDED_VIEWINFO) == false) { 328 // Extended view info was requested but the layoutlib does not support it. 329 // Add it manually. 330 List<ViewInfo> infoList = session.getRootViews(); 331 if (infoList != null) { 332 for (ViewInfo info : infoList) { 333 addExtendedViewInfo(info); 334 } 335 } 336 } 337 338 return session; 339 } else if (mLegacyBridge != null) { 340 return createLegacySession(params); 341 } 342 343 return null; 344 } 345 346 /** 347 * Renders a Drawable. If the rendering is successful, the result image is accessible through 348 * {@link Result#getData()}. It is of type {@link BufferedImage} 349 * @param params the rendering parameters. 350 * @return the result of the action. 351 */ 352 public Result renderDrawable(DrawableParams params) { 353 if (mBridge != null) { 354 return mBridge.renderDrawable(params); 355 } 356 357 return Status.NOT_IMPLEMENTED.createResult(); 358 } 359 360 /** 361 * Clears the resource cache for a specific project. 362 * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused 363 * until this method is called. 364 * <p/>The cache is not configuration dependent and should only be cleared when a 365 * resource changes (at this time only bitmaps and 9 patches go into the cache). 366 * 367 * @param projectKey the key for the project. 368 * 369 * @see Bridge#clearCaches(Object) 370 */ 371 public void clearCaches(Object projectKey) { 372 if (mBridge != null) { 373 mBridge.clearCaches(projectKey); 374 } else if (mLegacyBridge != null) { 375 mLegacyBridge.clearCaches(projectKey); 376 } 377 } 378 379 /** 380 * Utility method returning the parent of a given view object. 381 * 382 * @param viewObject the object for which to return the parent. 383 * 384 * @return a {@link Result} indicating the status of the action, and if success, the parent 385 * object in {@link Result#getData()} 386 */ 387 public Result getViewParent(Object viewObject) { 388 if (mBridge != null) { 389 Result r = mBridge.getViewParent(viewObject); 390 if (r.isSuccess()) { 391 return r; 392 } 393 } 394 395 return getViewParentWithReflection(viewObject); 396 } 397 398 /** 399 * Utility method returning the index of a given view in its parent. 400 * @param viewObject the object for which to return the index. 401 * 402 * @return a {@link Result} indicating the status of the action, and if success, the index in 403 * the parent in {@link Result#getData()} 404 */ 405 public Result getViewIndex(Object viewObject) { 406 if (mBridge != null) { 407 Result r = mBridge.getViewIndex(viewObject); 408 if (r.isSuccess()) { 409 return r; 410 } 411 } 412 413 return getViewIndexReflection(viewObject); 414 } 415 416 // ------ Implementation 417 418 private LayoutLibrary(Bridge bridge, ILayoutBridge legacyBridge, ClassLoader classLoader, 419 LoadStatus status, String message) { 420 mBridge = bridge; 421 mLegacyBridge = legacyBridge; 422 mClassLoader = classLoader; 423 mStatus = status; 424 mLoadMessage = message; 425 } 426 427 /** 428 * Returns the API level of the legacy bridge. 429 * <p/> 430 * This handles the case where ILayoutBridge does not have a {@link ILayoutBridge#getApiLevel()} 431 * (at API level 1). 432 * <p/> 433 * {@link ILayoutBridge#getApiLevel()} should never called directly. 434 * 435 * @return the api level of {@link #mLegacyBridge}. 436 */ 437 private int getLegacyApiLevel() { 438 int apiLevel = 1; 439 try { 440 apiLevel = mLegacyBridge.getApiLevel(); 441 } catch (AbstractMethodError e) { 442 // the first version of the api did not have this method 443 // so this is 1 444 } 445 446 return apiLevel; 447 } 448 449 private RenderSession createLegacySession(SessionParams params) { 450 if (params.getLayoutDescription() instanceof IXmlPullParser == false) { 451 throw new IllegalArgumentException("Parser must be of type ILegacyPullParser"); 452 } 453 if (params.getProjectCallback() instanceof 454 com.android.layoutlib.api.IProjectCallback == false) { 455 throw new IllegalArgumentException("Project callback must be of type ILegacyCallback"); 456 } 457 458 if (params.getResources() instanceof ResourceResolver == false) { 459 throw new IllegalArgumentException("RenderResources object must be of type ResourceResolver"); 460 } 461 462 ResourceResolver resources = (ResourceResolver) params.getResources(); 463 464 int apiLevel = getLegacyApiLevel(); 465 466 // create a log wrapper since the older api requires a ILayoutLog 467 final LayoutLog log = params.getLog(); 468 ILayoutLog logWrapper = new ILayoutLog() { 469 470 @Override 471 public void warning(String message) { 472 log.warning(null, message, null /*data*/); 473 } 474 475 @Override 476 public void error(Throwable t) { 477 log.error(null, "error!", t, null /*data*/); 478 } 479 480 @Override 481 public void error(String message) { 482 log.error(null, message, null /*data*/); 483 } 484 }; 485 486 487 // convert the map of ResourceValue into IResourceValue. Super ugly but works. 488 489 Map<String, Map<String, IResourceValue>> projectMap = convertMap( 490 resources.getProjectResources()); 491 Map<String, Map<String, IResourceValue>> frameworkMap = convertMap( 492 resources.getFrameworkResources()); 493 494 ILayoutResult result = null; 495 496 if (apiLevel == 4) { 497 // Final ILayoutBridge API added support for "render full height" 498 result = mLegacyBridge.computeLayout( 499 (IXmlPullParser) params.getLayoutDescription(), 500 params.getProjectKey(), 501 params.getScreenWidth(), params.getScreenHeight(), 502 params.getRenderingMode() == RenderingMode.FULL_EXPAND ? true : false, 503 params.getDensity().getDpiValue(), params.getXdpi(), params.getYdpi(), 504 resources.getThemeName(), resources.isProjectTheme(), 505 projectMap, frameworkMap, 506 (IProjectCallback) params.getProjectCallback(), 507 logWrapper); 508 } else if (apiLevel == 3) { 509 // api 3 add density support. 510 result = mLegacyBridge.computeLayout( 511 (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(), 512 params.getScreenWidth(), params.getScreenHeight(), 513 params.getDensity().getDpiValue(), params.getXdpi(), params.getYdpi(), 514 resources.getThemeName(), resources.isProjectTheme(), 515 projectMap, frameworkMap, 516 (IProjectCallback) params.getProjectCallback(), logWrapper); 517 } else if (apiLevel == 2) { 518 // api 2 added boolean for separation of project/framework theme 519 result = mLegacyBridge.computeLayout( 520 (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(), 521 params.getScreenWidth(), params.getScreenHeight(), 522 resources.getThemeName(), resources.isProjectTheme(), 523 projectMap, frameworkMap, 524 (IProjectCallback) params.getProjectCallback(), logWrapper); 525 } else { 526 // First api with no density/dpi, and project theme boolean mixed 527 // into the theme name. 528 529 // change the string if it's a custom theme to make sure we can 530 // differentiate them 531 String themeName = resources.getThemeName(); 532 if (resources.isProjectTheme()) { 533 themeName = "*" + themeName; //$NON-NLS-1$ 534 } 535 536 result = mLegacyBridge.computeLayout( 537 (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(), 538 params.getScreenWidth(), params.getScreenHeight(), 539 themeName, 540 projectMap, frameworkMap, 541 (IProjectCallback) params.getProjectCallback(), logWrapper); 542 } 543 544 // clean up that is not done by the ILayoutBridge itself 545 legacyCleanUp(); 546 547 return convertToScene(result); 548 } 549 550 @SuppressWarnings("unchecked") 551 private Map<String, Map<String, IResourceValue>> convertMap( 552 Map<ResourceType, Map<String, ResourceValue>> map) { 553 Map<String, Map<String, IResourceValue>> result = 554 new HashMap<String, Map<String, IResourceValue>>(); 555 556 for (Entry<ResourceType, Map<String, ResourceValue>> entry : map.entrySet()) { 557 // ugly case but works. 558 result.put(entry.getKey().getName(), 559 (Map) entry.getValue()); 560 } 561 562 return result; 563 } 564 565 /** 566 * Converts a {@link ILayoutResult} to a {@link RenderSession}. 567 */ 568 private RenderSession convertToScene(ILayoutResult result) { 569 570 Result sceneResult; 571 ViewInfo rootViewInfo = null; 572 573 if (result.getSuccess() == ILayoutResult.SUCCESS) { 574 sceneResult = Status.SUCCESS.createResult(); 575 ILayoutViewInfo oldRootView = result.getRootView(); 576 if (oldRootView != null) { 577 rootViewInfo = convertToViewInfo(oldRootView); 578 } 579 } else { 580 sceneResult = Status.ERROR_UNKNOWN.createResult(result.getErrorMessage()); 581 } 582 583 // create a BasicLayoutScene. This will return the given values but return the default 584 // implementation for all method. 585 // ADT should gracefully handle the default implementations of LayoutScene 586 return new StaticRenderSession(sceneResult, rootViewInfo, result.getImage()); 587 } 588 589 /** 590 * Converts a {@link ILayoutViewInfo} (and its children) to a {@link ViewInfo}. 591 */ 592 private ViewInfo convertToViewInfo(ILayoutViewInfo view) { 593 // create the view info. 594 ViewInfo viewInfo = new ViewInfo(view.getName(), view.getViewKey(), 595 view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); 596 597 // then convert the children 598 ILayoutViewInfo[] children = view.getChildren(); 599 if (children != null) { 600 ArrayList<ViewInfo> convertedChildren = new ArrayList<ViewInfo>(children.length); 601 for (ILayoutViewInfo child : children) { 602 convertedChildren.add(convertToViewInfo(child)); 603 } 604 viewInfo.setChildren(convertedChildren); 605 } 606 607 return viewInfo; 608 } 609 610 /** 611 * Post rendering clean-up that must be done here because it's not done in any layoutlib using 612 * {@link ILayoutBridge}. 613 */ 614 private void legacyCleanUp() { 615 try { 616 Class<?> looperClass = mClassLoader.loadClass("android.os.Looper"); //$NON-NLS-1$ 617 Field threadLocalField = looperClass.getField("sThreadLocal"); //$NON-NLS-1$ 618 if (threadLocalField != null) { 619 threadLocalField.setAccessible(true); 620 // get object. Field is static so no need to pass an object 621 ThreadLocal<?> threadLocal = (ThreadLocal<?>) threadLocalField.get(null); 622 if (threadLocal != null) { 623 threadLocal.remove(); 624 } 625 } 626 } catch (Exception e) { 627 // do nothing. 628 } 629 } 630 631 private Result getViewParentWithReflection(Object viewObject) { 632 // default implementation using reflection. 633 try { 634 if (mViewGetParentMethod == null) { 635 Class<?> viewClass = Class.forName("android.view.View"); 636 mViewGetParentMethod = viewClass.getMethod("getParent"); 637 } 638 639 return Status.SUCCESS.createResult(mViewGetParentMethod.invoke(viewObject)); 640 } catch (Exception e) { 641 // Catch all for the reflection calls. 642 return ERROR_REFLECTION.createResult(null, e); 643 } 644 } 645 646 /** 647 * Utility method returning the index of a given view in its parent. 648 * @param viewObject the object for which to return the index. 649 * 650 * @return a {@link Result} indicating the status of the action, and if success, the index in 651 * the parent in {@link Result#getData()} 652 */ 653 private Result getViewIndexReflection(Object viewObject) { 654 // default implementation using reflection. 655 try { 656 Class<?> viewClass = Class.forName("android.view.View"); 657 658 if (mViewGetParentMethod == null) { 659 mViewGetParentMethod = viewClass.getMethod("getParent"); 660 } 661 662 Object parentObject = mViewGetParentMethod.invoke(viewObject); 663 664 if (mViewParentIndexOfChildMethod == null) { 665 Class<?> viewParentClass = Class.forName("android.view.ViewParent"); 666 mViewParentIndexOfChildMethod = viewParentClass.getMethod("indexOfChild", 667 viewClass); 668 } 669 670 return Status.SUCCESS.createResult( 671 mViewParentIndexOfChildMethod.invoke(parentObject, viewObject)); 672 } catch (Exception e) { 673 // Catch all for the reflection calls. 674 return ERROR_REFLECTION.createResult(null, e); 675 } 676 } 677 678 private void addExtendedViewInfo(ViewInfo info) { 679 computeExtendedViewInfo(info); 680 681 List<ViewInfo> children = info.getChildren(); 682 for (ViewInfo child : children) { 683 addExtendedViewInfo(child); 684 } 685 } 686 687 private void computeExtendedViewInfo(ViewInfo info) { 688 Object viewObject = info.getViewObject(); 689 Object params = info.getLayoutParamsObject(); 690 691 int baseLine = getViewBaselineReflection(viewObject); 692 int leftMargin = 0; 693 int topMargin = 0; 694 int rightMargin = 0; 695 int bottomMargin = 0; 696 697 try { 698 if (mMarginLayoutParamClass == null) { 699 mMarginLayoutParamClass = Class.forName( 700 "android.view.ViewGroup$MarginLayoutParams"); 701 702 mLeftMarginField = mMarginLayoutParamClass.getField("leftMargin"); 703 mTopMarginField = mMarginLayoutParamClass.getField("topMargin"); 704 mRightMarginField = mMarginLayoutParamClass.getField("rightMargin"); 705 mBottomMarginField = mMarginLayoutParamClass.getField("bottomMargin"); 706 } 707 708 if (mMarginLayoutParamClass.isAssignableFrom(params.getClass())) { 709 710 leftMargin = (Integer)mLeftMarginField.get(params); 711 topMargin = (Integer)mTopMarginField.get(params); 712 rightMargin = (Integer)mRightMarginField.get(params); 713 bottomMargin = (Integer)mBottomMarginField.get(params); 714 } 715 716 } catch (Exception e) { 717 // just use 'unknown' value. 718 leftMargin = Integer.MIN_VALUE; 719 topMargin = Integer.MIN_VALUE; 720 rightMargin = Integer.MIN_VALUE; 721 bottomMargin = Integer.MIN_VALUE; 722 } 723 724 info.setExtendedInfo(baseLine, leftMargin, topMargin, rightMargin, bottomMargin); 725 } 726 727 /** 728 * Utility method returning the baseline value for a given view object. This basically returns 729 * View.getBaseline(). 730 * 731 * @param viewObject the object for which to return the index. 732 * 733 * @return the baseline value or -1 if not applicable to the view object or if this layout 734 * library does not implement this method. 735 */ 736 private int getViewBaselineReflection(Object viewObject) { 737 // default implementation using reflection. 738 try { 739 if (mViewGetBaselineMethod == null) { 740 Class<?> viewClass = Class.forName("android.view.View"); 741 mViewGetBaselineMethod = viewClass.getMethod("getBaseline"); 742 } 743 744 Object result = mViewGetBaselineMethod.invoke(viewObject); 745 if (result instanceof Integer) { 746 return ((Integer)result).intValue(); 747 } 748 749 } catch (Exception e) { 750 // Catch all for the reflection calls. 751 } 752 753 return Integer.MIN_VALUE; 754 } 755 } 756