1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php 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 package com.android.ide.eclipse.adt.internal.editors.layout.gle2; 17 18 import static com.android.SdkConstants.LAYOUT_RESOURCE_PREFIX; 19 20 import com.android.annotations.NonNull; 21 import com.android.ide.common.api.IClientRulesEngine; 22 import com.android.ide.common.api.INode; 23 import com.android.ide.common.api.Rect; 24 import com.android.ide.common.rendering.HardwareConfigHelper; 25 import com.android.ide.common.rendering.LayoutLibrary; 26 import com.android.ide.common.rendering.RenderSecurityManager; 27 import com.android.ide.common.rendering.api.Capability; 28 import com.android.ide.common.rendering.api.DrawableParams; 29 import com.android.ide.common.rendering.api.HardwareConfig; 30 import com.android.ide.common.rendering.api.IImageFactory; 31 import com.android.ide.common.rendering.api.ILayoutPullParser; 32 import com.android.ide.common.rendering.api.LayoutLog; 33 import com.android.ide.common.rendering.api.RenderSession; 34 import com.android.ide.common.rendering.api.ResourceValue; 35 import com.android.ide.common.rendering.api.Result; 36 import com.android.ide.common.rendering.api.SessionParams; 37 import com.android.ide.common.rendering.api.SessionParams.RenderingMode; 38 import com.android.ide.common.rendering.api.ViewInfo; 39 import com.android.ide.common.resources.ResourceResolver; 40 import com.android.ide.common.resources.configuration.FolderConfiguration; 41 import com.android.ide.eclipse.adt.AdtPlugin; 42 import com.android.ide.eclipse.adt.AdtUtils; 43 import com.android.ide.eclipse.adt.internal.editors.layout.ContextPullParser; 44 import com.android.ide.eclipse.adt.internal.editors.layout.ProjectCallback; 45 import com.android.ide.eclipse.adt.internal.editors.layout.UiElementPullParser; 46 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Configuration; 47 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.ConfigurationChooser; 48 import com.android.ide.eclipse.adt.internal.editors.layout.configuration.Locale; 49 import com.android.ide.eclipse.adt.internal.editors.layout.gle2.IncludeFinder.Reference; 50 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory; 51 import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy; 52 import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode; 53 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo; 54 import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestInfo.ActivityAttributes; 55 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode; 56 import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode; 57 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; 58 import com.android.ide.eclipse.adt.internal.sdk.Sdk; 59 import com.android.sdklib.IAndroidTarget; 60 import com.android.sdklib.devices.Device; 61 import com.google.common.base.Charsets; 62 import com.google.common.io.Files; 63 64 import org.eclipse.core.resources.IProject; 65 import org.xmlpull.v1.XmlPullParser; 66 import org.xmlpull.v1.XmlPullParserException; 67 68 import java.awt.Toolkit; 69 import java.awt.image.BufferedImage; 70 import java.io.File; 71 import java.io.IOException; 72 import java.io.StringReader; 73 import java.util.Collections; 74 import java.util.HashMap; 75 import java.util.List; 76 import java.util.Map; 77 import java.util.Set; 78 79 /** 80 * The {@link RenderService} provides rendering and layout information for 81 * Android layouts. This is a wrapper around the layout library. 82 */ 83 public class RenderService { 84 private static final Object RENDERING_LOCK = new Object(); 85 86 /** Reference to the file being edited. Can also be used to access the {@link IProject}. */ 87 private final GraphicalEditorPart mEditor; 88 89 // The following fields are inferred from the editor and not customizable by the 90 // client of the render service: 91 92 private final IProject mProject; 93 private final ProjectCallback mProjectCallback; 94 private final ResourceResolver mResourceResolver; 95 private final int mMinSdkVersion; 96 private final int mTargetSdkVersion; 97 private final LayoutLibrary mLayoutLib; 98 private final IImageFactory mImageFactory; 99 private final HardwareConfigHelper mHardwareConfigHelper; 100 private final Locale mLocale; 101 102 // The following fields are optional or configurable using the various chained 103 // setters: 104 105 private UiDocumentNode mModel; 106 private Reference mIncludedWithin; 107 private RenderingMode mRenderingMode = RenderingMode.NORMAL; 108 private LayoutLog mLogger; 109 private Integer mOverrideBgColor; 110 private boolean mShowDecorations = true; 111 private Set<UiElementNode> mExpandNodes = Collections.<UiElementNode>emptySet(); 112 private final Object mCredential; 113 114 /** Use the {@link #create} factory instead */ RenderService(GraphicalEditorPart editor, Object credential)115 private RenderService(GraphicalEditorPart editor, Object credential) { 116 mEditor = editor; 117 mCredential = credential; 118 119 mProject = editor.getProject(); 120 LayoutCanvas canvas = editor.getCanvasControl(); 121 mImageFactory = canvas.getImageOverlay(); 122 ConfigurationChooser chooser = editor.getConfigurationChooser(); 123 Configuration config = chooser.getConfiguration(); 124 FolderConfiguration folderConfig = config.getFullConfig(); 125 126 Device device = config.getDevice(); 127 assert device != null; // Should only attempt render with configuration that has device 128 mHardwareConfigHelper = new HardwareConfigHelper(device); 129 mHardwareConfigHelper.setOrientation( 130 folderConfig.getScreenOrientationQualifier().getValue()); 131 132 mLayoutLib = editor.getReadyLayoutLib(true /*displayError*/); 133 mResourceResolver = editor.getResourceResolver(); 134 mProjectCallback = editor.getProjectCallback(true /*reset*/, mLayoutLib); 135 mMinSdkVersion = editor.getMinSdkVersion(); 136 mTargetSdkVersion = editor.getTargetSdkVersion(); 137 mLocale = config.getLocale(); 138 } 139 RenderService(GraphicalEditorPart editor, Configuration configuration, ResourceResolver resourceResolver, Object credential)140 private RenderService(GraphicalEditorPart editor, 141 Configuration configuration, ResourceResolver resourceResolver, 142 Object credential) { 143 mEditor = editor; 144 mCredential = credential; 145 146 mProject = editor.getProject(); 147 LayoutCanvas canvas = editor.getCanvasControl(); 148 mImageFactory = canvas.getImageOverlay(); 149 FolderConfiguration folderConfig = configuration.getFullConfig(); 150 151 Device device = configuration.getDevice(); 152 assert device != null; 153 mHardwareConfigHelper = new HardwareConfigHelper(device); 154 mHardwareConfigHelper.setOrientation( 155 folderConfig.getScreenOrientationQualifier().getValue()); 156 157 mLayoutLib = editor.getReadyLayoutLib(true /*displayError*/); 158 mResourceResolver = resourceResolver != null ? resourceResolver : editor.getResourceResolver(); 159 mProjectCallback = editor.getProjectCallback(true /*reset*/, mLayoutLib); 160 mMinSdkVersion = editor.getMinSdkVersion(); 161 mTargetSdkVersion = editor.getTargetSdkVersion(); 162 mLocale = configuration.getLocale(); 163 } 164 createSecurityManager()165 private RenderSecurityManager createSecurityManager() { 166 String projectPath = null; 167 String sdkPath = null; 168 if (RenderSecurityManager.RESTRICT_READS) { 169 projectPath = AdtUtils.getAbsolutePath(mProject).toFile().getPath(); 170 Sdk sdk = Sdk.getCurrent(); 171 sdkPath = sdk != null ? sdk.getSdkOsLocation() : null; 172 } 173 RenderSecurityManager securityManager = new RenderSecurityManager(sdkPath, projectPath); 174 securityManager.setLogger(AdtPlugin.getDefault()); 175 176 // Make sure this is initialized before we attempt to use it from layoutlib 177 Toolkit.getDefaultToolkit(); 178 179 return securityManager; 180 } 181 182 /** 183 * Returns true if this configuration supports the given rendering 184 * capability 185 * 186 * @param target the target to look up the layout library for 187 * @param capability the capability to check 188 * @return true if the capability is supported 189 */ supports( @onNull IAndroidTarget target, @NonNull Capability capability)190 public static boolean supports( 191 @NonNull IAndroidTarget target, 192 @NonNull Capability capability) { 193 Sdk sdk = Sdk.getCurrent(); 194 if (sdk != null) { 195 AndroidTargetData targetData = sdk.getTargetData(target); 196 if (targetData != null) { 197 LayoutLibrary layoutLib = targetData.getLayoutLibrary(); 198 if (layoutLib != null) { 199 return layoutLib.supports(capability); 200 } 201 } 202 } 203 204 return false; 205 } 206 207 /** 208 * Creates a new {@link RenderService} associated with the given editor. 209 * 210 * @param editor the editor to provide configuration data such as the render target 211 * @return a {@link RenderService} which can perform rendering services 212 */ create(GraphicalEditorPart editor)213 public static RenderService create(GraphicalEditorPart editor) { 214 // Delegate to editor such that it can pass its credential to the service 215 return editor.createRenderService(); 216 } 217 218 /** 219 * Creates a new {@link RenderService} associated with the given editor. 220 * 221 * @param editor the editor to provide configuration data such as the render target 222 * @param credential the sandbox credential 223 * @return a {@link RenderService} which can perform rendering services 224 */ 225 @NonNull create(GraphicalEditorPart editor, Object credential)226 public static RenderService create(GraphicalEditorPart editor, Object credential) { 227 return new RenderService(editor, credential); 228 } 229 230 /** 231 * Creates a new {@link RenderService} associated with the given editor. 232 * 233 * @param editor the editor to provide configuration data such as the render target 234 * @param configuration the configuration to use (and fallback to editor for the rest) 235 * @param resolver a resource resolver to use to look up resources 236 * @return a {@link RenderService} which can perform rendering services 237 */ create(GraphicalEditorPart editor, Configuration configuration, ResourceResolver resolver)238 public static RenderService create(GraphicalEditorPart editor, 239 Configuration configuration, ResourceResolver resolver) { 240 // Delegate to editor such that it can pass its credential to the service 241 return editor.createRenderService(configuration, resolver); 242 } 243 244 /** 245 * Creates a new {@link RenderService} associated with the given editor. 246 * 247 * @param editor the editor to provide configuration data such as the render target 248 * @param configuration the configuration to use (and fallback to editor for the rest) 249 * @param resolver a resource resolver to use to look up resources 250 * @param credential the sandbox credential 251 * @return a {@link RenderService} which can perform rendering services 252 */ create(GraphicalEditorPart editor, Configuration configuration, ResourceResolver resolver, Object credential)253 public static RenderService create(GraphicalEditorPart editor, 254 Configuration configuration, ResourceResolver resolver, Object credential) { 255 return new RenderService(editor, configuration, resolver, credential); 256 } 257 258 /** 259 * Renders the given model, using this editor's theme and screen settings, and returns 260 * the result as a {@link RenderSession}. 261 * 262 * @param model the model to be rendered, which can be different than the editor's own 263 * {@link #getModel()}. 264 * @param width the width to use for the layout, or -1 to use the width of the screen 265 * associated with this editor 266 * @param height the height to use for the layout, or -1 to use the height of the screen 267 * associated with this editor 268 * @param explodeNodes a set of nodes to explode, or null for none 269 * @param overrideBgColor If non-null, use the given color as a background to render over 270 * rather than the normal background requested by the theme 271 * @param noDecor If true, don't draw window decorations like the system bar 272 * @param logger a logger where rendering errors are reported 273 * @param renderingMode the {@link RenderingMode} to use for rendering 274 * @return the resulting rendered image wrapped in an {@link RenderSession} 275 */ 276 277 /** 278 * Sets the {@link LayoutLog} to be used during rendering. If none is specified, a 279 * silent logger will be used. 280 * 281 * @param logger the log to be used 282 * @return this (such that chains of setters can be stringed together) 283 */ setLog(LayoutLog logger)284 public RenderService setLog(LayoutLog logger) { 285 mLogger = logger; 286 return this; 287 } 288 289 /** 290 * Sets the model to be rendered, which can be different than the editor's own 291 * {@link GraphicalEditorPart#getModel()}. 292 * 293 * @param model the model to be rendered 294 * @return this (such that chains of setters can be stringed together) 295 */ setModel(UiDocumentNode model)296 public RenderService setModel(UiDocumentNode model) { 297 mModel = model; 298 return this; 299 } 300 301 /** 302 * Overrides the width and height to be used during rendering (which might be adjusted if 303 * the {@link #setRenderingMode(RenderingMode)} is {@link RenderingMode#FULL_EXPAND}. 304 * 305 * A value of -1 will make the rendering use the normal width and height coming from the 306 * {@link Configuration#getDevice()} object. 307 * 308 * @param overrideRenderWidth the width in pixels of the layout to be rendered 309 * @param overrideRenderHeight the height in pixels of the layout to be rendered 310 * @return this (such that chains of setters can be stringed together) 311 */ setOverrideRenderSize(int overrideRenderWidth, int overrideRenderHeight)312 public RenderService setOverrideRenderSize(int overrideRenderWidth, int overrideRenderHeight) { 313 mHardwareConfigHelper.setOverrideRenderSize(overrideRenderWidth, overrideRenderHeight); 314 return this; 315 } 316 317 /** 318 * Sets the max width and height to be used during rendering (which might be adjusted if 319 * the {@link #setRenderingMode(RenderingMode)} is {@link RenderingMode#FULL_EXPAND}. 320 * 321 * A value of -1 will make the rendering use the normal width and height coming from the 322 * {@link Configuration#getDevice()} object. 323 * 324 * @param maxRenderWidth the max width in pixels of the layout to be rendered 325 * @param maxRenderHeight the max height in pixels of the layout to be rendered 326 * @return this (such that chains of setters can be stringed together) 327 */ setMaxRenderSize(int maxRenderWidth, int maxRenderHeight)328 public RenderService setMaxRenderSize(int maxRenderWidth, int maxRenderHeight) { 329 mHardwareConfigHelper.setMaxRenderSize(maxRenderWidth, maxRenderHeight); 330 return this; 331 } 332 333 /** 334 * Sets the {@link RenderingMode} to be used during rendering. If none is specified, 335 * the default is {@link RenderingMode#NORMAL}. 336 * 337 * @param renderingMode the rendering mode to be used 338 * @return this (such that chains of setters can be stringed together) 339 */ setRenderingMode(RenderingMode renderingMode)340 public RenderService setRenderingMode(RenderingMode renderingMode) { 341 mRenderingMode = renderingMode; 342 return this; 343 } 344 345 /** 346 * Sets the overriding background color to be used, if any. The color should be a 347 * bitmask of AARRGGBB. The default is null. 348 * 349 * @param overrideBgColor the overriding background color to be used in the rendering, 350 * in the form of a AARRGGBB bitmask, or null to use no custom background. 351 * @return this (such that chains of setters can be stringed together) 352 */ setOverrideBgColor(Integer overrideBgColor)353 public RenderService setOverrideBgColor(Integer overrideBgColor) { 354 mOverrideBgColor = overrideBgColor; 355 return this; 356 } 357 358 /** 359 * Sets whether the rendering should include decorations such as a system bar, an 360 * application bar etc depending on the SDK target and theme. The default is true. 361 * 362 * @param showDecorations true if the rendering should include system bars etc. 363 * @return this (such that chains of setters can be stringed together) 364 */ setDecorations(boolean showDecorations)365 public RenderService setDecorations(boolean showDecorations) { 366 mShowDecorations = showDecorations; 367 return this; 368 } 369 370 /** 371 * Sets the nodes to expand during rendering. These will be padded with approximately 372 * 20 pixels and also highlighted by the {@link EmptyViewsOverlay}. The default is an 373 * empty collection. 374 * 375 * @param nodesToExpand the nodes to be expanded 376 * @return this (such that chains of setters can be stringed together) 377 */ setNodesToExpand(Set<UiElementNode> nodesToExpand)378 public RenderService setNodesToExpand(Set<UiElementNode> nodesToExpand) { 379 mExpandNodes = nodesToExpand; 380 return this; 381 } 382 383 /** 384 * Sets the {@link Reference} to an outer layout that this layout should be rendered 385 * within. The outer layout <b>must</b> contain an include tag which points to this 386 * layout. The default is null. 387 * 388 * @param includedWithin a reference to an outer layout to render this layout within 389 * @return this (such that chains of setters can be stringed together) 390 */ setIncludedWithin(Reference includedWithin)391 public RenderService setIncludedWithin(Reference includedWithin) { 392 mIncludedWithin = includedWithin; 393 return this; 394 } 395 396 /** Initializes any remaining optional fields after all setters have been called */ finishConfiguration()397 private void finishConfiguration() { 398 if (mLogger == null) { 399 // Silent logging 400 mLogger = new LayoutLog(); 401 } 402 } 403 404 /** 405 * Renders the model and returns the result as a {@link RenderSession}. 406 * @return the {@link RenderSession} resulting from rendering the current model 407 */ createRenderSession()408 public RenderSession createRenderSession() { 409 assert mModel != null : "Incomplete service config"; 410 finishConfiguration(); 411 412 if (mResourceResolver == null) { 413 // Abort the rendering if the resources are not found. 414 return null; 415 } 416 417 HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig(); 418 419 UiElementPullParser modelParser = new UiElementPullParser(mModel, 420 false, mExpandNodes, hardwareConfig.getDensity(), mProject); 421 ILayoutPullParser topParser = modelParser; 422 423 // Code to support editing included layout 424 // first reset the layout parser just in case. 425 mProjectCallback.setLayoutParser(null, null); 426 427 if (mIncludedWithin != null) { 428 // Outer layout name: 429 String contextLayoutName = mIncludedWithin.getName(); 430 431 // Find the layout file. 432 ResourceValue contextLayout = mResourceResolver.findResValue( 433 LAYOUT_RESOURCE_PREFIX + contextLayoutName, false /* forceFrameworkOnly*/); 434 if (contextLayout != null) { 435 File layoutFile = new File(contextLayout.getValue()); 436 if (layoutFile.isFile()) { 437 try { 438 // Get the name of the layout actually being edited, without the extension 439 // as it's what IXmlPullParser.getParser(String) will receive. 440 String queryLayoutName = mEditor.getLayoutResourceName(); 441 mProjectCallback.setLayoutParser(queryLayoutName, modelParser); 442 topParser = new ContextPullParser(mProjectCallback, layoutFile); 443 topParser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 444 String xmlText = Files.toString(layoutFile, Charsets.UTF_8); 445 topParser.setInput(new StringReader(xmlText)); 446 } catch (IOException e) { 447 AdtPlugin.log(e, null); 448 } catch (XmlPullParserException e) { 449 AdtPlugin.log(e, null); 450 } 451 } 452 } 453 } 454 455 SessionParams params = new SessionParams( 456 topParser, 457 mRenderingMode, 458 mProject /* projectKey */, 459 hardwareConfig, 460 mResourceResolver, 461 mProjectCallback, 462 mMinSdkVersion, 463 mTargetSdkVersion, 464 mLogger); 465 466 // Request margin and baseline information. 467 // TODO: Be smarter about setting this; start without it, and on the first request 468 // for an extended view info, re-render in the same session, and then set a flag 469 // which will cause this to create extended view info each time from then on in the 470 // same session 471 params.setExtendedViewInfoMode(true); 472 473 params.setLocale(mLocale.toLocaleId()); 474 475 ManifestInfo manifestInfo = ManifestInfo.get(mProject); 476 try { 477 params.setRtlSupport(manifestInfo.isRtlSupported()); 478 } catch (Exception e) { 479 // ignore. 480 } 481 if (!mShowDecorations) { 482 params.setForceNoDecor(); 483 } else { 484 try { 485 params.setAppLabel(manifestInfo.getApplicationLabel()); 486 params.setAppIcon(manifestInfo.getApplicationIcon()); 487 String activity = mEditor.getConfigurationChooser().getConfiguration().getActivity(); 488 if (activity != null) { 489 ActivityAttributes info = manifestInfo.getActivityAttributes(activity); 490 if (info != null) { 491 if (info.getLabel() != null) { 492 params.setAppLabel(info.getLabel()); 493 } 494 if (info.getIcon() != null) { 495 params.setAppIcon(info.getIcon()); 496 } 497 } 498 } 499 } catch (Exception e) { 500 // ignore. 501 } 502 } 503 504 if (mOverrideBgColor != null) { 505 params.setOverrideBgColor(mOverrideBgColor.intValue()); 506 } 507 508 // set the Image Overlay as the image factory. 509 params.setImageFactory(mImageFactory); 510 511 mProjectCallback.setLogger(mLogger); 512 mProjectCallback.setResourceResolver(mResourceResolver); 513 RenderSecurityManager securityManager = createSecurityManager(); 514 try { 515 securityManager.setActive(true, mCredential); 516 synchronized (RENDERING_LOCK) { 517 return mLayoutLib.createSession(params); 518 } 519 } catch (RuntimeException t) { 520 // Exceptions from the bridge 521 mLogger.error(null, t.getLocalizedMessage(), t, null); 522 throw t; 523 } finally { 524 securityManager.dispose(mCredential); 525 mProjectCallback.setLogger(null); 526 mProjectCallback.setResourceResolver(null); 527 } 528 } 529 530 /** 531 * Renders the given resource value (which should refer to a drawable) and returns it 532 * as an image 533 * 534 * @param drawableResourceValue the drawable resource value to be rendered, or null 535 * @return the image, or null if something went wrong 536 */ renderDrawable(ResourceValue drawableResourceValue)537 public BufferedImage renderDrawable(ResourceValue drawableResourceValue) { 538 if (drawableResourceValue == null) { 539 return null; 540 } 541 542 finishConfiguration(); 543 544 HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig(); 545 546 DrawableParams params = new DrawableParams(drawableResourceValue, mProject, hardwareConfig, 547 mResourceResolver, mProjectCallback, mMinSdkVersion, 548 mTargetSdkVersion, mLogger); 549 params.setForceNoDecor(); 550 Result result = mLayoutLib.renderDrawable(params); 551 if (result != null && result.isSuccess()) { 552 Object data = result.getData(); 553 if (data instanceof BufferedImage) { 554 return (BufferedImage) data; 555 } 556 } 557 558 return null; 559 } 560 561 /** 562 * Measure the children of the given parent node, applying the given filter to the 563 * pull parser's attribute values. 564 * 565 * @param parent the parent node to measure children for 566 * @param filter the filter to apply to the attribute values 567 * @return a map from node children of the parent to new bounds of the nodes 568 */ measureChildren(INode parent, final IClientRulesEngine.AttributeFilter filter)569 public Map<INode, Rect> measureChildren(INode parent, 570 final IClientRulesEngine.AttributeFilter filter) { 571 finishConfiguration(); 572 HardwareConfig hardwareConfig = mHardwareConfigHelper.getConfig(); 573 574 final NodeFactory mNodeFactory = mEditor.getCanvasControl().getNodeFactory(); 575 UiElementNode parentNode = ((NodeProxy) parent).getNode(); 576 UiElementPullParser topParser = new UiElementPullParser(parentNode, 577 false, Collections.<UiElementNode>emptySet(), hardwareConfig.getDensity(), 578 mProject) { 579 @Override 580 public String getAttributeValue(String namespace, String localName) { 581 if (filter != null) { 582 Object cookie = getViewCookie(); 583 if (cookie instanceof UiViewElementNode) { 584 NodeProxy node = mNodeFactory.create((UiViewElementNode) cookie); 585 if (node != null) { 586 String value = filter.getAttribute(node, namespace, localName); 587 if (value != null) { 588 return value; 589 } 590 // null means no preference, not "unset". 591 } 592 } 593 } 594 595 return super.getAttributeValue(namespace, localName); 596 } 597 598 /** 599 * The parser usually assumes that the top level node is a document node that 600 * should be skipped, and that's not the case when we render in the middle of 601 * the tree, so override {@link UiElementPullParser#onNextFromStartDocument} 602 * to change this behavior 603 */ 604 @Override 605 public void onNextFromStartDocument() { 606 mParsingState = START_TAG; 607 } 608 }; 609 610 SessionParams params = new SessionParams( 611 topParser, 612 RenderingMode.FULL_EXPAND, 613 mProject /* projectKey */, 614 hardwareConfig, 615 mResourceResolver, 616 mProjectCallback, 617 mMinSdkVersion, 618 mTargetSdkVersion, 619 mLogger); 620 params.setLayoutOnly(); 621 params.setForceNoDecor(); 622 623 RenderSession session = null; 624 mProjectCallback.setLogger(mLogger); 625 mProjectCallback.setResourceResolver(mResourceResolver); 626 RenderSecurityManager securityManager = createSecurityManager(); 627 try { 628 securityManager.setActive(true, mCredential); 629 synchronized (RENDERING_LOCK) { 630 session = mLayoutLib.createSession(params); 631 } 632 if (session.getResult().isSuccess()) { 633 assert session.getRootViews().size() == 1; 634 ViewInfo root = session.getRootViews().get(0); 635 List<ViewInfo> children = root.getChildren(); 636 Map<INode, Rect> map = new HashMap<INode, Rect>(children.size()); 637 for (ViewInfo info : children) { 638 if (info.getCookie() instanceof UiViewElementNode) { 639 UiViewElementNode uiNode = (UiViewElementNode) info.getCookie(); 640 NodeProxy node = mNodeFactory.create(uiNode); 641 map.put(node, new Rect(info.getLeft(), info.getTop(), 642 info.getRight() - info.getLeft(), 643 info.getBottom() - info.getTop())); 644 } 645 } 646 647 return map; 648 } 649 } catch (RuntimeException t) { 650 // Exceptions from the bridge 651 mLogger.error(null, t.getLocalizedMessage(), t, null); 652 throw t; 653 } finally { 654 securityManager.dispose(mCredential); 655 mProjectCallback.setLogger(null); 656 mProjectCallback.setResourceResolver(null); 657 if (session != null) { 658 session.dispose(); 659 } 660 } 661 662 return null; 663 } 664 } 665