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