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