Home | History | Annotate | Download | only in gle2
      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 */
    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 
    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 
    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      */
    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      */
    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
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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      */
    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 */
    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      */
    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      */
    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      */
    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