LayoutLibrary.java revision 6837aad30d6c51783ca1dc784ca6bdcc8a3d9f2d
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
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
17package com.android.ide.common.rendering;
18
19import static com.android.ide.common.rendering.api.Result.Status.ERROR_REFLECTION;
20
21import com.android.ide.common.rendering.api.Bridge;
22import com.android.ide.common.rendering.api.Capability;
23import com.android.ide.common.rendering.api.DrawableParams;
24import com.android.ide.common.rendering.api.ILayoutPullParser;
25import com.android.ide.common.rendering.api.LayoutLog;
26import com.android.ide.common.rendering.api.RenderSession;
27import com.android.ide.common.rendering.api.ResourceValue;
28import com.android.ide.common.rendering.api.Result;
29import com.android.ide.common.rendering.api.Result.Status;
30import com.android.ide.common.rendering.api.SessionParams;
31import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
32import com.android.ide.common.rendering.api.ViewInfo;
33import com.android.ide.common.rendering.legacy.ILegacyPullParser;
34import com.android.ide.common.rendering.legacy.LegacyCallback;
35import com.android.ide.common.resources.ResourceResolver;
36import com.android.ide.common.sdk.LoadStatus;
37import com.android.layoutlib.api.ILayoutBridge;
38import com.android.layoutlib.api.ILayoutLog;
39import com.android.layoutlib.api.ILayoutResult;
40import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
41import com.android.layoutlib.api.IProjectCallback;
42import com.android.layoutlib.api.IResourceValue;
43import com.android.layoutlib.api.IXmlPullParser;
44import com.android.resources.ResourceType;
45import com.android.utils.ILogger;
46
47import java.awt.image.BufferedImage;
48import java.io.File;
49import java.lang.reflect.Constructor;
50import java.lang.reflect.Field;
51import java.lang.reflect.Method;
52import java.net.URI;
53import java.net.URL;
54import java.net.URLClassLoader;
55import java.util.ArrayList;
56import java.util.HashMap;
57import java.util.List;
58import java.util.Map;
59import java.util.Map.Entry;
60
61/**
62 * Class to use the Layout library.
63 * <p/>
64 * Use {@link #load(String, ILogger)} to load the jar file.
65 * <p/>
66 * Use the layout library with:
67 * {@link #init(String, Map)}, {@link #supports(Capability)}, {@link #createSession(SessionParams)},
68 * {@link #dispose()}, {@link #clearCaches(Object)}.
69 *
70 * <p/>
71 * For client wanting to access both new and old (pre API level 5) layout libraries, it is
72 * important that the following interfaces be used:<br>
73 * {@link ILegacyPullParser} instead of {@link ILayoutPullParser}<br>
74 * {@link LegacyCallback} instead of {@link com.android.ide.common.rendering.api.IProjectCallback}.
75 * <p/>
76 * These interfaces will ensure that both new and older Layout libraries can be accessed.
77 */
78@SuppressWarnings("deprecation")
79public class LayoutLibrary {
80
81    public final static String CLASS_BRIDGE = "com.android.layoutlib.bridge.Bridge"; //$NON-NLS-1$
82
83    /** Link to the layout bridge */
84    private final Bridge mBridge;
85    /** Link to a ILayoutBridge in case loaded an older library */
86    private final ILayoutBridge mLegacyBridge;
87    /** Status of the layoutlib.jar loading */
88    private final LoadStatus mStatus;
89    /** Message associated with the {@link LoadStatus}. This is mostly used when
90     * {@link #getStatus()} returns {@link LoadStatus#FAILED}.
91     */
92    private final String mLoadMessage;
93    /** classloader used to load the jar file */
94    private final ClassLoader mClassLoader;
95
96    // Reflection data for older Layout Libraries.
97    private Method mViewGetParentMethod;
98    private Method mViewGetBaselineMethod;
99    private Method mViewParentIndexOfChildMethod;
100    private Class<?> mMarginLayoutParamClass;
101    private Field mLeftMarginField;
102    private Field mTopMarginField;
103    private Field mRightMarginField;
104    private Field mBottomMarginField;
105
106    /**
107     * Returns the {@link LoadStatus} of the loading of the layoutlib jar file.
108     */
109    public LoadStatus getStatus() {
110        return mStatus;
111    }
112
113    /** Returns the message associated with the {@link LoadStatus}. This is mostly used when
114     * {@link #getStatus()} returns {@link LoadStatus#FAILED}.
115     */
116    public String getLoadMessage() {
117        return mLoadMessage;
118    }
119
120    /**
121     * Returns the classloader used to load the classes in the layoutlib jar file.
122     */
123    public ClassLoader getClassLoader() {
124        return mClassLoader;
125    }
126
127    /**
128     * Loads the layoutlib.jar file located at the given path and returns a {@link LayoutLibrary}
129     * object representing the result.
130     * <p/>
131     * If loading failed {@link #getStatus()} will reflect this, and {@link #getBridge()} will
132     * return null.
133     *
134     * @param layoutLibJarOsPath the path of the jar file
135     * @param log an optional log file.
136     * @return a {@link LayoutLibrary} object always.
137     */
138    public static LayoutLibrary load(String layoutLibJarOsPath, ILogger log, String toolName) {
139
140        LoadStatus status = LoadStatus.LOADING;
141        String message = null;
142        Bridge bridge = null;
143        ILayoutBridge legacyBridge = null;
144        ClassLoader classLoader = null;
145
146        try {
147            // get the URL for the file.
148            File f = new File(layoutLibJarOsPath);
149            if (f.isFile() == false) {
150                if (log != null) {
151                    log.error(null, "layoutlib.jar is missing!"); //$NON-NLS-1$
152                }
153            } else {
154                URI uri = f.toURI();
155                URL url = uri.toURL();
156
157                // create a class loader. Because this jar reference interfaces
158                // that are in the editors plugin, it's important to provide
159                // a parent class loader.
160                classLoader = new URLClassLoader(
161                        new URL[] { url },
162                        LayoutLibrary.class.getClassLoader());
163
164                // load the class
165                Class<?> clazz = classLoader.loadClass(CLASS_BRIDGE);
166                if (clazz != null) {
167                    // instantiate an object of the class.
168                    Constructor<?> constructor = clazz.getConstructor();
169                    if (constructor != null) {
170                        Object bridgeObject = constructor.newInstance();
171                        if (bridgeObject instanceof Bridge) {
172                            bridge = (Bridge)bridgeObject;
173                        } else if (bridgeObject instanceof ILayoutBridge) {
174                            legacyBridge = (ILayoutBridge) bridgeObject;
175                        }
176                    }
177                }
178
179                if (bridge == null && legacyBridge == null) {
180                    status = LoadStatus.FAILED;
181                    message = "Failed to load " + CLASS_BRIDGE; //$NON-NLS-1$
182                    if (log != null) {
183                        log.error(null,
184                                "Failed to load " + //$NON-NLS-1$
185                                CLASS_BRIDGE +
186                                " from " +          //$NON-NLS-1$
187                                layoutLibJarOsPath);
188                    }
189                } else {
190                    // mark the lib as loaded, unless it's overridden below.
191                    status = LoadStatus.LOADED;
192
193                    // check the API, only if it's not a legacy bridge
194                    if (bridge != null) {
195                        int api = bridge.getApiLevel();
196                        if (api > Bridge.API_CURRENT) {
197                            status = LoadStatus.FAILED;
198                            message = String.format(
199                                    "This version of the rendering library is more recent than your version of %1$s. Please update %1$s", toolName);
200                        }
201                    }
202                }
203            }
204        } catch (Throwable t) {
205            status = LoadStatus.FAILED;
206            Throwable cause = t;
207            while (cause.getCause() != null) {
208                cause = cause.getCause();
209            }
210            message = "Failed to load the LayoutLib: " + cause.getMessage();
211            // log the error.
212            if (log != null) {
213                log.error(t, message);
214            }
215        }
216
217        return new LayoutLibrary(bridge, legacyBridge, classLoader, status, message);
218    }
219
220    // ------ Layout Lib API proxy
221
222    /**
223     * Returns the API level of the layout library.
224     */
225    public int getApiLevel() {
226        if (mBridge != null) {
227            return mBridge.getApiLevel();
228        }
229
230        if (mLegacyBridge != null) {
231            return getLegacyApiLevel();
232        }
233
234        return 0;
235    }
236
237    /**
238     * Returns the revision of the library inside a given (layoutlib) API level.
239     * The true version number of the library is {@link #getApiLevel()}.{@link #getRevision()}
240     */
241    public int getRevision() {
242        if (mBridge != null) {
243            return mBridge.getRevision();
244        }
245
246        return 0;
247    }
248
249    /**
250     * Returns whether the LayoutLibrary supports a given {@link Capability}.
251     * @return true if it supports it.
252     *
253     * @see Bridge#getCapabilities()
254     *
255     */
256    public boolean supports(Capability capability) {
257        if (mBridge != null) {
258            return mBridge.getCapabilities().contains(capability);
259        }
260
261        if (mLegacyBridge != null) {
262            switch (capability) {
263                case UNBOUND_RENDERING:
264                    // legacy stops at 4. 5 is new API.
265                    return getLegacyApiLevel() == 4;
266            }
267        }
268
269        return false;
270    }
271
272    /**
273     * Initializes the Layout Library object. This must be called before any other action is taken
274     * on the instance.
275     *
276     * @param platformProperties The build properties for the platform.
277     * @param fontLocation the location of the fonts in the SDK target.
278     * @param enumValueMap map attrName => { map enumFlagName => Integer value }. This is typically
279     *          read from attrs.xml in the SDK target.
280     * @param log a {@link LayoutLog} object. Can be null.
281     * @return true if success.
282     *
283     * @see Bridge#init(String, Map)
284     */
285    public boolean init(Map<String, String> platformProperties,
286            File fontLocation,
287            Map<String, Map<String, Integer>> enumValueMap,
288            LayoutLog log) {
289        if (mBridge != null) {
290            return mBridge.init(platformProperties, fontLocation, enumValueMap, log);
291        } else if (mLegacyBridge != null) {
292            return mLegacyBridge.init(fontLocation.getAbsolutePath(), enumValueMap);
293        }
294
295        return false;
296    }
297
298    /**
299     * Prepares the layoutlib to unloaded.
300     *
301     * @see Bridge#dispose()
302     */
303    public boolean dispose() {
304        if (mBridge != null) {
305            return mBridge.dispose();
306        }
307
308        return true;
309    }
310
311    /**
312     * Starts a layout session by inflating and rendering it. The method returns a
313     * {@link RenderSession} on which further actions can be taken.
314     * <p/>
315     * Before taking further actions on the scene, it is recommended to use
316     * {@link #supports(Capability)} to check what the scene can do.
317     *
318     * @return a new {@link ILayoutScene} object that contains the result of the scene creation and
319     * first rendering or null if {@link #getStatus()} doesn't return {@link LoadStatus#LOADED}.
320     *
321     * @see Bridge#createSession(SessionParams)
322     */
323    public RenderSession createSession(SessionParams params) {
324        if (mBridge != null) {
325            RenderSession session = mBridge.createSession(params);
326            if (params.getExtendedViewInfoMode() &&
327                    mBridge.getCapabilities().contains(Capability.EXTENDED_VIEWINFO) == false) {
328                // Extended view info was requested but the layoutlib does not support it.
329                // Add it manually.
330                List<ViewInfo> infoList = session.getRootViews();
331                if (infoList != null) {
332                    for (ViewInfo info : infoList) {
333                        addExtendedViewInfo(info);
334                    }
335                }
336            }
337
338            return session;
339        } else if (mLegacyBridge != null) {
340            return createLegacySession(params);
341        }
342
343        return null;
344    }
345
346    /**
347     * Renders a Drawable. If the rendering is successful, the result image is accessible through
348     * {@link Result#getData()}. It is of type {@link BufferedImage}
349     * @param params the rendering parameters.
350     * @return the result of the action.
351     */
352    public Result renderDrawable(DrawableParams params) {
353        if (mBridge != null) {
354            return mBridge.renderDrawable(params);
355        }
356
357        return Status.NOT_IMPLEMENTED.createResult();
358    }
359
360    /**
361     * Clears the resource cache for a specific project.
362     * <p/>This cache contains bitmaps and nine patches that are loaded from the disk and reused
363     * until this method is called.
364     * <p/>The cache is not configuration dependent and should only be cleared when a
365     * resource changes (at this time only bitmaps and 9 patches go into the cache).
366     *
367     * @param projectKey the key for the project.
368     *
369     * @see Bridge#clearCaches(Object)
370     */
371    public void clearCaches(Object projectKey) {
372        if (mBridge != null) {
373            mBridge.clearCaches(projectKey);
374        } else if (mLegacyBridge != null) {
375            mLegacyBridge.clearCaches(projectKey);
376        }
377    }
378
379    /**
380     * Utility method returning the parent of a given view object.
381     *
382     * @param viewObject the object for which to return the parent.
383     *
384     * @return a {@link Result} indicating the status of the action, and if success, the parent
385     *      object in {@link Result#getData()}
386     */
387    public Result getViewParent(Object viewObject) {
388        if (mBridge != null) {
389            Result r = mBridge.getViewParent(viewObject);
390            if (r.isSuccess()) {
391                return r;
392            }
393        }
394
395        return getViewParentWithReflection(viewObject);
396    }
397
398    /**
399     * Utility method returning the index of a given view in its parent.
400     * @param viewObject the object for which to return the index.
401     *
402     * @return a {@link Result} indicating the status of the action, and if success, the index in
403     *      the parent in {@link Result#getData()}
404     */
405    public Result getViewIndex(Object viewObject) {
406        if (mBridge != null) {
407            Result r = mBridge.getViewIndex(viewObject);
408            if (r.isSuccess()) {
409                return r;
410            }
411        }
412
413        return getViewIndexReflection(viewObject);
414    }
415
416    // ------ Implementation
417
418    private LayoutLibrary(Bridge bridge, ILayoutBridge legacyBridge, ClassLoader classLoader,
419            LoadStatus status, String message) {
420        mBridge = bridge;
421        mLegacyBridge = legacyBridge;
422        mClassLoader = classLoader;
423        mStatus = status;
424        mLoadMessage = message;
425    }
426
427    /**
428     * Returns the API level of the legacy bridge.
429     * <p/>
430     * This handles the case where ILayoutBridge does not have a {@link ILayoutBridge#getApiLevel()}
431     * (at API level 1).
432     * <p/>
433     * {@link ILayoutBridge#getApiLevel()} should never called directly.
434     *
435     * @return the api level of {@link #mLegacyBridge}.
436     */
437    private int getLegacyApiLevel() {
438        int apiLevel = 1;
439        try {
440            apiLevel = mLegacyBridge.getApiLevel();
441        } catch (AbstractMethodError e) {
442            // the first version of the api did not have this method
443            // so this is 1
444        }
445
446        return apiLevel;
447    }
448
449    private RenderSession createLegacySession(SessionParams params) {
450        if (params.getLayoutDescription() instanceof IXmlPullParser == false) {
451            throw new IllegalArgumentException("Parser must be of type ILegacyPullParser");
452        }
453        if (params.getProjectCallback() instanceof
454                com.android.layoutlib.api.IProjectCallback == false) {
455            throw new IllegalArgumentException("Project callback must be of type ILegacyCallback");
456        }
457
458        if (params.getResources() instanceof ResourceResolver == false) {
459            throw new IllegalArgumentException("RenderResources object must be of type ResourceResolver");
460        }
461
462        ResourceResolver resources = (ResourceResolver) params.getResources();
463
464        int apiLevel = getLegacyApiLevel();
465
466        // create a log wrapper since the older api requires a ILayoutLog
467        final LayoutLog log = params.getLog();
468        ILayoutLog logWrapper = new ILayoutLog() {
469
470            @Override
471            public void warning(String message) {
472                log.warning(null, message, null /*data*/);
473            }
474
475            @Override
476            public void error(Throwable t) {
477                log.error(null, "error!", t, null /*data*/);
478            }
479
480            @Override
481            public void error(String message) {
482                log.error(null, message, null /*data*/);
483            }
484        };
485
486
487        // convert the map of ResourceValue into IResourceValue. Super ugly but works.
488
489        Map<String, Map<String, IResourceValue>> projectMap = convertMap(
490                resources.getProjectResources());
491        Map<String, Map<String, IResourceValue>> frameworkMap = convertMap(
492                resources.getFrameworkResources());
493
494        ILayoutResult result = null;
495
496        if (apiLevel == 4) {
497            // Final ILayoutBridge API added support for "render full height"
498            result = mLegacyBridge.computeLayout(
499                    (IXmlPullParser) params.getLayoutDescription(),
500                    params.getProjectKey(),
501                    params.getScreenWidth(), params.getScreenHeight(),
502                    params.getRenderingMode() == RenderingMode.FULL_EXPAND ? true : false,
503                    params.getDensity().getDpiValue(), params.getXdpi(), params.getYdpi(),
504                    resources.getThemeName(), resources.isProjectTheme(),
505                    projectMap, frameworkMap,
506                    (IProjectCallback) params.getProjectCallback(),
507                    logWrapper);
508        } else if (apiLevel == 3) {
509            // api 3 add density support.
510            result = mLegacyBridge.computeLayout(
511                    (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(),
512                    params.getScreenWidth(), params.getScreenHeight(),
513                    params.getDensity().getDpiValue(), params.getXdpi(), params.getYdpi(),
514                    resources.getThemeName(), resources.isProjectTheme(),
515                    projectMap, frameworkMap,
516                    (IProjectCallback) params.getProjectCallback(), logWrapper);
517        } else if (apiLevel == 2) {
518            // api 2 added boolean for separation of project/framework theme
519            result = mLegacyBridge.computeLayout(
520                    (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(),
521                    params.getScreenWidth(), params.getScreenHeight(),
522                    resources.getThemeName(), resources.isProjectTheme(),
523                    projectMap, frameworkMap,
524                    (IProjectCallback) params.getProjectCallback(), logWrapper);
525        } else {
526            // First api with no density/dpi, and project theme boolean mixed
527            // into the theme name.
528
529            // change the string if it's a custom theme to make sure we can
530            // differentiate them
531            String themeName = resources.getThemeName();
532            if (resources.isProjectTheme()) {
533                themeName = "*" + themeName; //$NON-NLS-1$
534            }
535
536            result = mLegacyBridge.computeLayout(
537                    (IXmlPullParser) params.getLayoutDescription(), params.getProjectKey(),
538                    params.getScreenWidth(), params.getScreenHeight(),
539                    themeName,
540                    projectMap, frameworkMap,
541                    (IProjectCallback) params.getProjectCallback(), logWrapper);
542        }
543
544        // clean up that is not done by the ILayoutBridge itself
545        legacyCleanUp();
546
547        return convertToScene(result);
548    }
549
550    @SuppressWarnings("unchecked")
551    private Map<String, Map<String, IResourceValue>> convertMap(
552            Map<ResourceType, Map<String, ResourceValue>> map) {
553        Map<String, Map<String, IResourceValue>> result =
554            new HashMap<String, Map<String, IResourceValue>>();
555
556        for (Entry<ResourceType, Map<String, ResourceValue>> entry : map.entrySet()) {
557            // ugly case but works.
558            result.put(entry.getKey().getName(),
559                    (Map) entry.getValue());
560        }
561
562        return result;
563    }
564
565    /**
566     * Converts a {@link ILayoutResult} to a {@link RenderSession}.
567     */
568    private RenderSession convertToScene(ILayoutResult result) {
569
570        Result sceneResult;
571        ViewInfo rootViewInfo = null;
572
573        if (result.getSuccess() == ILayoutResult.SUCCESS) {
574            sceneResult = Status.SUCCESS.createResult();
575            ILayoutViewInfo oldRootView = result.getRootView();
576            if (oldRootView != null) {
577                rootViewInfo = convertToViewInfo(oldRootView);
578            }
579        } else {
580            sceneResult = Status.ERROR_UNKNOWN.createResult(result.getErrorMessage());
581        }
582
583        // create a BasicLayoutScene. This will return the given values but return the default
584        // implementation for all method.
585        // ADT should gracefully handle the default implementations of LayoutScene
586        return new StaticRenderSession(sceneResult, rootViewInfo, result.getImage());
587    }
588
589    /**
590     * Converts a {@link ILayoutViewInfo} (and its children) to a {@link ViewInfo}.
591     */
592    private ViewInfo convertToViewInfo(ILayoutViewInfo view) {
593        // create the view info.
594        ViewInfo viewInfo = new ViewInfo(view.getName(), view.getViewKey(),
595                view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
596
597        // then convert the children
598        ILayoutViewInfo[] children = view.getChildren();
599        if (children != null) {
600            ArrayList<ViewInfo> convertedChildren = new ArrayList<ViewInfo>(children.length);
601            for (ILayoutViewInfo child : children) {
602                convertedChildren.add(convertToViewInfo(child));
603            }
604            viewInfo.setChildren(convertedChildren);
605        }
606
607        return viewInfo;
608    }
609
610    /**
611     * Post rendering clean-up that must be done here because it's not done in any layoutlib using
612     * {@link ILayoutBridge}.
613     */
614    private void legacyCleanUp() {
615        try {
616            Class<?> looperClass = mClassLoader.loadClass("android.os.Looper"); //$NON-NLS-1$
617            Field threadLocalField = looperClass.getField("sThreadLocal"); //$NON-NLS-1$
618            if (threadLocalField != null) {
619                threadLocalField.setAccessible(true);
620                // get object. Field is static so no need to pass an object
621                ThreadLocal<?> threadLocal = (ThreadLocal<?>) threadLocalField.get(null);
622                if (threadLocal != null) {
623                    threadLocal.remove();
624                }
625            }
626        } catch (Exception e) {
627            // do nothing.
628        }
629    }
630
631    private Result getViewParentWithReflection(Object viewObject) {
632        // default implementation using reflection.
633        try {
634            if (mViewGetParentMethod == null) {
635                Class<?> viewClass = Class.forName("android.view.View");
636                mViewGetParentMethod = viewClass.getMethod("getParent");
637            }
638
639            return Status.SUCCESS.createResult(mViewGetParentMethod.invoke(viewObject));
640        } catch (Exception e) {
641            // Catch all for the reflection calls.
642            return ERROR_REFLECTION.createResult(null, e);
643        }
644    }
645
646    /**
647     * Utility method returning the index of a given view in its parent.
648     * @param viewObject the object for which to return the index.
649     *
650     * @return a {@link Result} indicating the status of the action, and if success, the index in
651     *      the parent in {@link Result#getData()}
652     */
653    private Result getViewIndexReflection(Object viewObject) {
654        // default implementation using reflection.
655        try {
656            Class<?> viewClass = Class.forName("android.view.View");
657
658            if (mViewGetParentMethod == null) {
659                mViewGetParentMethod = viewClass.getMethod("getParent");
660            }
661
662            Object parentObject = mViewGetParentMethod.invoke(viewObject);
663
664            if (mViewParentIndexOfChildMethod == null) {
665                Class<?> viewParentClass = Class.forName("android.view.ViewParent");
666                mViewParentIndexOfChildMethod = viewParentClass.getMethod("indexOfChild",
667                        viewClass);
668            }
669
670            return Status.SUCCESS.createResult(
671                    mViewParentIndexOfChildMethod.invoke(parentObject, viewObject));
672        } catch (Exception e) {
673            // Catch all for the reflection calls.
674            return ERROR_REFLECTION.createResult(null, e);
675        }
676    }
677
678    private void addExtendedViewInfo(ViewInfo info) {
679        computeExtendedViewInfo(info);
680
681        List<ViewInfo> children = info.getChildren();
682        for (ViewInfo child : children) {
683            addExtendedViewInfo(child);
684        }
685    }
686
687    private void computeExtendedViewInfo(ViewInfo info) {
688        Object viewObject = info.getViewObject();
689        Object params = info.getLayoutParamsObject();
690
691        int baseLine = getViewBaselineReflection(viewObject);
692        int leftMargin = 0;
693        int topMargin = 0;
694        int rightMargin = 0;
695        int bottomMargin = 0;
696
697        try {
698            if (mMarginLayoutParamClass == null) {
699                mMarginLayoutParamClass = Class.forName(
700                        "android.view.ViewGroup$MarginLayoutParams");
701
702                mLeftMarginField = mMarginLayoutParamClass.getField("leftMargin");
703                mTopMarginField = mMarginLayoutParamClass.getField("topMargin");
704                mRightMarginField = mMarginLayoutParamClass.getField("rightMargin");
705                mBottomMarginField = mMarginLayoutParamClass.getField("bottomMargin");
706            }
707
708            if (mMarginLayoutParamClass.isAssignableFrom(params.getClass())) {
709
710                leftMargin = (Integer)mLeftMarginField.get(params);
711                topMargin = (Integer)mTopMarginField.get(params);
712                rightMargin = (Integer)mRightMarginField.get(params);
713                bottomMargin = (Integer)mBottomMarginField.get(params);
714            }
715
716        } catch (Exception e) {
717            // just use 'unknown' value.
718            leftMargin = Integer.MIN_VALUE;
719            topMargin = Integer.MIN_VALUE;
720            rightMargin = Integer.MIN_VALUE;
721            bottomMargin = Integer.MIN_VALUE;
722        }
723
724        info.setExtendedInfo(baseLine, leftMargin, topMargin, rightMargin, bottomMargin);
725    }
726
727    /**
728     * Utility method returning the baseline value for a given view object. This basically returns
729     * View.getBaseline().
730     *
731     * @param viewObject the object for which to return the index.
732     *
733     * @return the baseline value or -1 if not applicable to the view object or if this layout
734     *     library does not implement this method.
735     */
736    private int getViewBaselineReflection(Object viewObject) {
737        // default implementation using reflection.
738        try {
739            if (mViewGetBaselineMethod == null) {
740                Class<?> viewClass = Class.forName("android.view.View");
741                mViewGetBaselineMethod = viewClass.getMethod("getBaseline");
742            }
743
744            Object result = mViewGetBaselineMethod.invoke(viewObject);
745            if (result instanceof Integer) {
746                return ((Integer)result).intValue();
747            }
748
749        } catch (Exception e) {
750            // Catch all for the reflection calls.
751        }
752
753        return Integer.MIN_VALUE;
754    }
755}
756