Bridge.java revision d24b8183b93e781080b2c16c487e60d51c12da31
1/*
2 * Copyright (C) 2008 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.layoutlib.bridge;
18
19import com.android.internal.util.XmlUtils;
20import com.android.layoutlib.api.ILayoutBridge;
21import com.android.layoutlib.api.ILayoutLog;
22import com.android.layoutlib.api.ILayoutResult;
23import com.android.layoutlib.api.IProjectCallback;
24import com.android.layoutlib.api.IResourceValue;
25import com.android.layoutlib.api.IStyleResourceValue;
26import com.android.layoutlib.api.IXmlPullParser;
27import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo;
28import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo;
29import com.android.ninepatch.NinePatch;
30import com.android.tools.layoutlib.create.MethodAdapter;
31import com.android.tools.layoutlib.create.OverrideMethod;
32
33import android.graphics.Bitmap;
34import android.graphics.Rect;
35import android.graphics.Region;
36import android.graphics.Typeface;
37import android.graphics.drawable.Drawable;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.ParcelFileDescriptor;
42import android.os.RemoteException;
43import android.util.TypedValue;
44import android.view.BridgeInflater;
45import android.view.IWindow;
46import android.view.IWindowSession;
47import android.view.KeyEvent;
48import android.view.MotionEvent;
49import android.view.Surface;
50import android.view.SurfaceView;
51import android.view.View;
52import android.view.ViewGroup;
53import android.view.View.AttachInfo;
54import android.view.View.MeasureSpec;
55import android.view.WindowManager.LayoutParams;
56import android.widget.FrameLayout;
57
58import java.lang.reflect.Field;
59import java.lang.reflect.Modifier;
60import java.util.Collection;
61import java.util.HashMap;
62import java.util.Map;
63
64/**
65 * Main entry point of the LayoutLib Bridge.
66 * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call
67 * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}.
68 */
69public final class Bridge implements ILayoutBridge {
70
71    private static final int DEFAULT_TITLE_BAR_HEIGHT = 25;
72    private static final int DEFAULT_STATUS_BAR_HEIGHT = 25;
73
74    public static class StaticMethodNotImplementedException extends RuntimeException {
75        public StaticMethodNotImplementedException(String msg) {
76            super(msg);
77        }
78    }
79
80    /**
81     * Maps from id to resource name/type.
82     */
83    private final static Map<Integer, String[]> sRMap = new HashMap<Integer, String[]>();
84    /**
85     * Same as sRMap except for int[] instead of int resources.
86     */
87    private final static Map<int[], String> sRArrayMap = new HashMap<int[], String>();
88    /**
89     * Reverse map compared to sRMap, resource type -> (resource name -> id)
90     */
91    private final static Map<String, Map<String, Integer>> sRFullMap =
92        new HashMap<String, Map<String,Integer>>();
93
94    private final static Map<Object, Map<String, Bitmap>> sProjectBitmapCache =
95        new HashMap<Object, Map<String, Bitmap>>();
96    private final static Map<Object, Map<String, NinePatch>> sProject9PatchCache =
97        new HashMap<Object, Map<String, NinePatch>>();
98
99    private final static Map<String, Bitmap> sFrameworkBitmapCache = new HashMap<String, Bitmap>();
100    private final static Map<String, NinePatch> sFramework9PatchCache =
101        new HashMap<String, NinePatch>();
102
103    private static Map<String, Map<String, Integer>> sEnumValueMap;
104
105    /**
106     * A default logger than prints to stdout/stderr.
107     */
108    private final static ILayoutLog sDefaultLogger = new ILayoutLog() {
109        public void error(String message) {
110            System.err.println(message);
111        }
112
113        public void error(Throwable t) {
114            String message = t.getMessage();
115            if (message == null) {
116                message = t.getClass().getName();
117            }
118
119            System.err.println(message);
120        }
121
122        public void warning(String message) {
123            System.out.println(message);
124        }
125    };
126
127    /**
128     * Logger defined during a compute layout operation.
129     * <p/>
130     * This logger is generally set to {@link #sDefaultLogger} except during rendering
131     * operations when it might be set to a specific provided logger.
132     * <p/>
133     * To change this value, use a block synchronized on {@link #sDefaultLogger}.
134     */
135    private static ILayoutLog sLogger = sDefaultLogger;
136
137    /*
138     * (non-Javadoc)
139     * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel()
140     */
141    public int getApiLevel() {
142        return API_CURRENT;
143    }
144
145    /*
146     * (non-Javadoc)
147     * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map)
148     */
149    public boolean init(
150            String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) {
151
152        return sinit(fontOsLocation, enumValueMap);
153    }
154
155    private static synchronized boolean sinit(String fontOsLocation,
156            Map<String, Map<String, Integer>> enumValueMap) {
157
158        // When DEBUG_LAYOUT is set and is not 0 or false, setup a default listener
159        // on static (native) methods which prints the signature on the console and
160        // throws an exception.
161        // This is useful when testing the rendering in ADT to identify static native
162        // methods that are ignored -- layoutlib_create makes them returns 0/false/null
163        // which is generally OK yet might be a problem, so this is how you'd find out.
164        //
165        // Currently layoutlib_create only overrides static native method.
166        // Static non-natives are not overridden and thus do not get here.
167        final String debug = System.getenv("DEBUG_LAYOUT");
168        if (debug != null && !debug.equals("0") && !debug.equals("false")) {
169
170            OverrideMethod.setDefaultListener(new MethodAdapter() {
171                @Override
172                public void onInvokeV(String signature, boolean isNative, Object caller) {
173                    if (sLogger != null) {
174                        synchronized (sDefaultLogger) {
175                            sLogger.error("Missing Stub: " + signature +
176                                    (isNative ? " (native)" : ""));
177                        }
178                    }
179
180                    if (debug.equalsIgnoreCase("throw")) {
181                        // Throwing this exception doesn't seem that useful. It breaks
182                        // the layout editor yet doesn't display anything meaningful to the
183                        // user. Having the error in the console is just as useful. We'll
184                        // throw it only if the environment variable is "throw" or "THROW".
185                        throw new StaticMethodNotImplementedException(signature);
186                    }
187                }
188            });
189        }
190
191        // Override View.isInEditMode to return true.
192        //
193        // This allows custom views that are drawn in the Graphical Layout Editor to adapt their
194        // rendering for preview. Most important this let custom views know that they can't expect
195        // the rest of their activities to be alive.
196        OverrideMethod.setMethodListener("android.view.View#isInEditMode()Z",
197            new MethodAdapter() {
198                @Override
199                public int onInvokeI(String signature, boolean isNative, Object caller) {
200                    return 1;
201                }
202            }
203        );
204
205        // load the fonts.
206        FontLoader fontLoader = FontLoader.create(fontOsLocation);
207        if (fontLoader != null) {
208            Typeface.init(fontLoader);
209        } else {
210            return false;
211        }
212
213        sEnumValueMap = enumValueMap;
214
215        // now parse com.android.internal.R (and only this one as android.R is a subset of
216        // the internal version), and put the content in the maps.
217        try {
218            // WARNING: this only works because the class is already loaded, and therefore
219            // the objects returned by Field.get() are the same as the ones used by
220            // the code accessing the R class.
221            // int[] does not implement equals/hashCode, and if the parsing used a different class
222            // loader for the R class, this would NOT work.
223            Class<?> r = com.android.internal.R.class;
224
225            for (Class<?> inner : r.getDeclaredClasses()) {
226                String resType = inner.getSimpleName();
227
228                Map<String, Integer> fullMap = new HashMap<String, Integer>();
229                sRFullMap.put(resType, fullMap);
230
231                for (Field f : inner.getDeclaredFields()) {
232                    // only process static final fields. Since the final attribute may have
233                    // been altered by layoutlib_create, we only check static
234                    int modifiers = f.getModifiers();
235                    if (Modifier.isStatic(modifiers)) {
236                        Class<?> type = f.getType();
237                        if (type.isArray() && type.getComponentType() == int.class) {
238                            // if the object is an int[] we put it in sRArrayMap
239                            sRArrayMap.put((int[]) f.get(null), f.getName());
240                        } else if (type == int.class) {
241                            Integer value = (Integer) f.get(null);
242                            sRMap.put(value, new String[] { f.getName(), resType });
243                            fullMap.put(f.getName(), value);
244                        } else {
245                            assert false;
246                        }
247                    }
248                }
249            }
250        } catch (IllegalArgumentException e) {
251            // FIXME: log/return the error (there's no logger object at this point!)
252            e.printStackTrace();
253            return false;
254        } catch (IllegalAccessException e) {
255            e.printStackTrace();
256            return false;
257        }
258
259        return true;
260    }
261
262    /*
263     * For compatilibty purposes, we implement the old deprecated version of computeLayout.
264     * (non-Javadoc)
265     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
266     */
267    @Deprecated
268    public ILayoutResult computeLayout(IXmlPullParser layoutDescription,
269            Object projectKey,
270            int screenWidth, int screenHeight, String themeName,
271            Map<String, Map<String, IResourceValue>> projectResources,
272            Map<String, Map<String, IResourceValue>> frameworkResources,
273            IProjectCallback customViewLoader, ILayoutLog logger) {
274        boolean isProjectTheme = false;
275        if (themeName.charAt(0) == '*') {
276            themeName = themeName.substring(1);
277            isProjectTheme = true;
278        }
279
280        return computeLayout(layoutDescription, projectKey, screenWidth, screenHeight, themeName, isProjectTheme,
281                projectResources, frameworkResources, customViewLoader, logger);
282    }
283
284    /*
285     * (non-Javadoc)
286     * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog)
287     */
288    public ILayoutResult computeLayout(IXmlPullParser layoutDescription,
289            Object projectKey,
290            int screenWidth, int screenHeight, String themeName, boolean isProjectTheme,
291            Map<String, Map<String, IResourceValue>> projectResources,
292            Map<String, Map<String, IResourceValue>> frameworkResources,
293            IProjectCallback customViewLoader, ILayoutLog logger) {
294        if (logger == null) {
295            logger = sDefaultLogger;
296        }
297
298        synchronized (sDefaultLogger) {
299            sLogger = logger;
300        }
301
302        // find the current theme and compute the style inheritance map
303        Map<IStyleResourceValue, IStyleResourceValue> styleParentMap =
304            new HashMap<IStyleResourceValue, IStyleResourceValue>();
305
306        IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme,
307                projectResources.get(BridgeConstants.RES_STYLE),
308                frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap);
309
310        BridgeContext context = null;
311        try {
312            context = new BridgeContext(projectKey, currentTheme, projectResources,
313                    frameworkResources, styleParentMap, customViewLoader, logger);
314            BridgeInflater inflater = new BridgeInflater(context, customViewLoader);
315            context.setBridgeInflater(inflater);
316
317            IResourceValue windowBackground = null;
318            int screenOffset = 0;
319            if (currentTheme != null) {
320                windowBackground = context.findItemInStyle(currentTheme, "windowBackground");
321                windowBackground = context.resolveResValue(windowBackground);
322
323                screenOffset = getScreenOffset(currentTheme, context);
324            }
325
326            // we need to make sure the Looper has been initialized for this thread.
327            // this is required for View that creates Handler objects.
328            if (Looper.myLooper() == null) {
329                Looper.prepare();
330            }
331
332            BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription,
333                    context, false /* platformResourceFlag */);
334
335            ViewGroup root = new FrameLayout(context);
336
337            View view = inflater.inflate(parser, root);
338
339            // set the AttachInfo on the root view.
340            AttachInfo info = new AttachInfo(new WindowSession(), new Window(),
341                    new Handler(), null);
342            info.mHasWindowFocus = true;
343            info.mWindowVisibility = View.VISIBLE;
344            info.mInTouchMode = false; // this is so that we can display selections.
345            root.dispatchAttachedToWindow(info, 0);
346
347            // get the background drawable
348            if (windowBackground != null) {
349                Drawable d = ResourceHelper.getDrawable(windowBackground.getValue(),
350                        context, true /* isFramework */);
351                root.setBackgroundDrawable(d);
352            }
353
354            int w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY);
355            int h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset,
356                    MeasureSpec.EXACTLY);
357
358            // measure the views
359            view.measure(w_spec, h_spec);
360            view.layout(0, screenOffset, screenWidth, screenHeight);
361
362            // draw them
363            BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset,
364                    logger);
365
366            root.draw(canvas);
367            canvas.dispose();
368
369            return new LayoutResult(visit(((ViewGroup)view).getChildAt(0), context),
370                    canvas.getImage());
371        } catch (Throwable e) {
372            // get the real cause of the exception.
373            Throwable t = e;
374            while (t.getCause() != null) {
375                t = t.getCause();
376            }
377
378            // log it
379            logger.error(t);
380
381            // then return with an ERROR status and the message from the real exception
382            return new LayoutResult(ILayoutResult.ERROR,
383                    t.getClass().getSimpleName() + ": " + t.getMessage());
384        } finally {
385            // Make sure to remove static references, otherwise we could not unload the lib
386            BridgeResources.clearSystem();
387            BridgeAssetManager.clearSystem();
388
389            // Remove the global logger
390            synchronized (sDefaultLogger) {
391                sLogger = sDefaultLogger;
392            }
393        }
394    }
395
396    /*
397     * (non-Javadoc)
398     * @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object)
399     */
400    public void clearCaches(Object projectKey) {
401        if (projectKey != null) {
402            sProjectBitmapCache.remove(projectKey);
403            sProject9PatchCache.remove(projectKey);
404        }
405    }
406
407    /**
408     * Returns details of a framework resource from its integer value.
409     * @param value the integer value
410     * @return an array of 2 strings containing the resource name and type, or null if the id
411     * does not match any resource.
412     */
413    public static String[] resolveResourceValue(int value) {
414        return sRMap.get(value);
415
416    }
417
418    /**
419     * Returns the name of a framework resource whose value is an int array.
420     * @param array
421     */
422    public static String resolveResourceValue(int[] array) {
423        return sRArrayMap.get(array);
424    }
425
426    /**
427     * Returns the integer id of a framework resource, from a given resource type and resource name.
428     * @param type the type of the resource
429     * @param name the name of the resource.
430     * @return an {@link Integer} containing the resource id, or null if no resource were found.
431     */
432    public static Integer getResourceValue(String type, String name) {
433        Map<String, Integer> map = sRFullMap.get(type);
434        if (map != null) {
435            return map.get(name);
436        }
437
438        return null;
439    }
440
441    static Map<String, Integer> getEnumValues(String attributeName) {
442        if (sEnumValueMap != null) {
443            return sEnumValueMap.get(attributeName);
444        }
445
446        return null;
447    }
448
449    /**
450     * Visits a View and its children and generate a {@link ILayoutViewInfo} containing the
451     * bounds of all the views.
452     * @param view the root View
453     * @param context the context.
454     */
455    private ILayoutViewInfo visit(View view, BridgeContext context) {
456        if (view == null) {
457            return null;
458        }
459
460        LayoutViewInfo result = new LayoutViewInfo(view.getClass().getName(),
461                context.getViewKey(view),
462                view.getLeft(), view.getTop(), view.getRight(), view.getBottom());
463
464        if (view instanceof ViewGroup) {
465            ViewGroup group = ((ViewGroup) view);
466            int n = group.getChildCount();
467            ILayoutViewInfo[] children = new ILayoutViewInfo[n];
468            for (int i = 0; i < group.getChildCount(); i++) {
469                children[i] = visit(group.getChildAt(i), context);
470            }
471            result.setChildren(children);
472        }
473
474        return result;
475    }
476
477    /**
478     * Compute style information from the given list of style for the project and framework.
479     * @param themeName the name of the current theme.  In order to differentiate project and
480     * platform themes sharing the same name, all project themes must be prepended with
481     * a '*' character.
482     * @param isProjectTheme Is this a project theme
483     * @param inProjectStyleMap the project style map
484     * @param inFrameworkStyleMap the framework style map
485     * @param outInheritanceMap the map of style inheritance. This is filled by the method
486     * @return the {@link IStyleResourceValue} matching <var>themeName</var>
487     */
488    private IStyleResourceValue computeStyleMaps(
489            String themeName, boolean isProjectTheme, Map<String,
490            IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap,
491            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
492
493        if (inProjectStyleMap != null && inFrameworkStyleMap != null) {
494            // first, get the theme
495            IResourceValue theme = null;
496
497            // project theme names have been prepended with a *
498            if (isProjectTheme) {
499                theme = inProjectStyleMap.get(themeName);
500            } else {
501                theme = inFrameworkStyleMap.get(themeName);
502            }
503
504            if (theme instanceof IStyleResourceValue) {
505                // compute the inheritance map for both the project and framework styles
506                computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap,
507                        inFrameworkStyleMap, outInheritanceMap);
508
509                // Compute the style inheritance for the framework styles/themes.
510                // Since, for those, the style parent values do not contain 'android:'
511                // we want to force looking in the framework style only to avoid using
512                // similarly named styles from the project.
513                // To do this, we pass null in lieu of the project style map.
514                computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */,
515                        inFrameworkStyleMap, outInheritanceMap);
516
517                return (IStyleResourceValue)theme;
518            }
519        }
520
521        return null;
522    }
523
524    /**
525     * Compute the parent style for all the styles in a given list.
526     * @param styles the styles for which we compute the parent.
527     * @param inProjectStyleMap the map of project styles.
528     * @param inFrameworkStyleMap the map of framework styles.
529     * @param outInheritanceMap the map of style inheritance. This is filled by the method.
530     */
531    private void computeStyleInheritance(Collection<IResourceValue> styles,
532            Map<String, IResourceValue> inProjectStyleMap,
533            Map<String, IResourceValue> inFrameworkStyleMap,
534            Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) {
535        for (IResourceValue value : styles) {
536            if (value instanceof IStyleResourceValue) {
537                IStyleResourceValue style = (IStyleResourceValue)value;
538                IStyleResourceValue parentStyle = null;
539
540                // first look for a specified parent.
541                String parentName = style.getParentStyle();
542
543                // no specified parent? try to infer it from the name of the style.
544                if (parentName == null) {
545                    parentName = getParentName(value.getName());
546                }
547
548                if (parentName != null) {
549                    parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap);
550
551                    if (parentStyle != null) {
552                        outInheritanceMap.put(style, parentStyle);
553                    }
554                }
555            }
556        }
557    }
558
559    /**
560     * Searches for and returns the {@link IStyleResourceValue} from a given name.
561     * <p/>The format of the name can be:
562     * <ul>
563     * <li>[android:]&lt;name&gt;</li>
564     * <li>[android:]style/&lt;name&gt;</li>
565     * <li>@[android:]style/&lt;name&gt;</li>
566     * </ul>
567     * @param parentName the name of the style.
568     * @param inProjectStyleMap the project style map. Can be <code>null</code>
569     * @param inFrameworkStyleMap the framework style map.
570     * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found.
571     */
572    private IStyleResourceValue getStyle(String parentName,
573            Map<String, IResourceValue> inProjectStyleMap,
574            Map<String, IResourceValue> inFrameworkStyleMap) {
575        boolean frameworkOnly = false;
576
577        String name = parentName;
578
579        // remove the useless @ if it's there
580        if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
581            name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
582        }
583
584        // check for framework identifier.
585        if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) {
586            frameworkOnly = true;
587            name = name.substring(BridgeConstants.PREFIX_ANDROID.length());
588        }
589
590        // at this point we could have the format style/<name>. we want only the name
591        if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) {
592            name = name.substring(BridgeConstants.REFERENCE_STYLE.length());
593        }
594
595        IResourceValue parent = null;
596
597        // if allowed, search in the project resources.
598        if (frameworkOnly == false && inProjectStyleMap != null) {
599            parent = inProjectStyleMap.get(name);
600        }
601
602        // if not found, then look in the framework resources.
603        if (parent == null) {
604            parent = inFrameworkStyleMap.get(name);
605        }
606
607        // make sure the result is the proper class type and return it.
608        if (parent instanceof IStyleResourceValue) {
609            return (IStyleResourceValue)parent;
610        }
611
612        sLogger.error(String.format("Unable to resolve parent style name: ", parentName));
613
614        return null;
615    }
616
617    /**
618     * Computes the name of the parent style, or <code>null</code> if the style is a root style.
619     */
620    private String getParentName(String styleName) {
621        int index = styleName.lastIndexOf('.');
622        if (index != -1) {
623            return styleName.substring(0, index);
624        }
625
626        return null;
627    }
628
629    /**
630     * Returns the top screen offset. This depends on whether the current theme defines the user
631     * of the title and status bars.
632     * @return the pixel height offset
633     */
634    private int getScreenOffset(IStyleResourceValue currentTheme, BridgeContext context) {
635        int offset = 0;
636
637        // get the title bar flag from the current theme.
638        IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle");
639
640        // because it may reference something else, we resolve it.
641        value = context.resolveResValue(value);
642
643        // if there's a value and it's true (default is false)
644        if (value == null || value.getValue() == null ||
645                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
646            // get value from the theme.
647            value = context.findItemInStyle(currentTheme, "windowTitleSize");
648
649            // resolve it
650            value = context.resolveResValue(value);
651
652            // default value
653            offset = DEFAULT_TITLE_BAR_HEIGHT;
654
655            // get the real value;
656            if (value != null) {
657                TypedValue typedValue = ResourceHelper.getValue(value.getValue());
658                if (typedValue != null) {
659                    offset = (int)typedValue.getDimension(context.getResources().mMetrics);
660                }
661            }
662        }
663
664        // get the fullscreen flag from the current theme.
665        value = context.findItemInStyle(currentTheme, "windowFullscreen");
666
667        // because it may reference something else, we resolve it.
668        value = context.resolveResValue(value);
669
670        if (value == null || value.getValue() == null ||
671                XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) {
672            // FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it.
673            offset += DEFAULT_STATUS_BAR_HEIGHT;
674        }
675
676        return offset;
677    }
678
679    /**
680     * Returns the bitmap for a specific path, from a specific project cache, or from the
681     * framework cache.
682     * @param value the path of the bitmap
683     * @param projectKey the key of the project, or null to query the framework cache.
684     * @return the cached Bitmap or null if not found.
685     */
686    static Bitmap getCachedBitmap(String value, Object projectKey) {
687        if (projectKey != null) {
688            Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
689            if (map != null) {
690                return map.get(value);
691            }
692
693            return null;
694        }
695
696        return sFrameworkBitmapCache.get(value);
697    }
698
699    /**
700     * Sets a bitmap in a project cache or in the framework cache.
701     * @param value the path of the bitmap
702     * @param bmp the Bitmap object
703     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
704     */
705    static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) {
706        if (projectKey != null) {
707            Map<String, Bitmap> map = sProjectBitmapCache.get(projectKey);
708
709            if (map == null) {
710                map = new HashMap<String, Bitmap>();
711                sProjectBitmapCache.put(projectKey, map);
712            }
713
714            map.put(value, bmp);
715        }
716
717        sFrameworkBitmapCache.put(value, bmp);
718    }
719
720    /**
721     * Returns the 9 patch for a specific path, from a specific project cache, or from the
722     * framework cache.
723     * @param value the path of the 9 patch
724     * @param projectKey the key of the project, or null to query the framework cache.
725     * @return the cached 9 patch or null if not found.
726     */
727    static NinePatch getCached9Patch(String value, Object projectKey) {
728        if (projectKey != null) {
729            Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
730
731            if (map != null) {
732                return map.get(value);
733            }
734
735            return null;
736        }
737
738        return sFramework9PatchCache.get(value);
739    }
740
741    /**
742     * Sets a 9 patch in a project cache or in the framework cache.
743     * @param value the path of the 9 patch
744     * @param ninePatch the 9 patch object
745     * @param projectKey the key of the project, or null to put the bitmap in the framework cache.
746     */
747    static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) {
748        if (projectKey != null) {
749            Map<String, NinePatch> map = sProject9PatchCache.get(projectKey);
750
751            if (map == null) {
752                map = new HashMap<String, NinePatch>();
753                sProject9PatchCache.put(projectKey, map);
754            }
755
756            map.put(value, ninePatch);
757        }
758
759        sFramework9PatchCache.put(value, ninePatch);
760    }
761
762    /**
763     * Implementation of {@link IWindowSession} so that mSession is not null in
764     * the {@link SurfaceView}.
765     */
766    private static final class WindowSession implements IWindowSession {
767
768        @SuppressWarnings("unused")
769        public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3)
770                throws RemoteException {
771            // pass for now.
772            return 0;
773        }
774
775        @SuppressWarnings("unused")
776        public void finishDrawing(IWindow arg0) throws RemoteException {
777            // pass for now.
778        }
779
780        @SuppressWarnings("unused")
781        public void finishKey(IWindow arg0) throws RemoteException {
782            // pass for now.
783        }
784
785        @SuppressWarnings("unused")
786        public boolean getInTouchMode() throws RemoteException {
787            // pass for now.
788            return false;
789        }
790
791        @SuppressWarnings("unused")
792        public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException {
793            // pass for now.
794            return null;
795        }
796
797        @SuppressWarnings("unused")
798        public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException {
799            // pass for now.
800            return null;
801        }
802
803        @SuppressWarnings("unused")
804        public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4,
805                boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Surface arg8)
806                throws RemoteException {
807            // pass for now.
808            return 0;
809        }
810
811        public void getDisplayFrame(IWindow window, Rect outDisplayFrame) {
812            // pass for now.
813        }
814
815        @SuppressWarnings("unused")
816        public void remove(IWindow arg0) throws RemoteException {
817            // pass for now.
818        }
819
820        @SuppressWarnings("unused")
821        public void setInTouchMode(boolean arg0) throws RemoteException {
822            // pass for now.
823        }
824
825        @SuppressWarnings("unused")
826        public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException {
827            // pass for now.
828        }
829
830        public void setInsets(IWindow window, int touchable, Rect contentInsets,
831                Rect visibleInsets) {
832            // pass for now.
833        }
834
835        public IBinder asBinder() {
836            // pass for now.
837            return null;
838        }
839    }
840
841    /**
842     * Implementation of {@link IWindow} to pass to the {@link AttachInfo}.
843     */
844    private static final class Window implements IWindow {
845
846        @SuppressWarnings("unused")
847        public void dispatchAppVisibility(boolean arg0) throws RemoteException {
848            // pass for now.
849        }
850
851        @SuppressWarnings("unused")
852        public void dispatchGetNewSurface() throws RemoteException {
853            // pass for now.
854        }
855
856        @SuppressWarnings("unused")
857        public void dispatchKey(KeyEvent arg0) throws RemoteException {
858            // pass for now.
859        }
860
861        @SuppressWarnings("unused")
862        public void dispatchPointer(MotionEvent arg0, long arg1) throws RemoteException {
863            // pass for now.
864        }
865
866        @SuppressWarnings("unused")
867        public void dispatchTrackball(MotionEvent arg0, long arg1) throws RemoteException {
868            // pass for now.
869        }
870
871        @SuppressWarnings("unused")
872        public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2)
873                throws RemoteException {
874            // pass for now.
875        }
876
877        @SuppressWarnings("unused")
878        public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4)
879                throws RemoteException {
880            // pass for now.
881        }
882
883        @SuppressWarnings("unused")
884        public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException {
885            // pass for now.
886        }
887
888        public IBinder asBinder() {
889            // pass for now.
890            return null;
891        }
892    }
893
894}
895