• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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