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