BridgeContext.java revision d7cbf3f72c73ba01293f6676453352ef60df3778
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.android;
18
19import com.android.ide.common.rendering.api.IProjectCallback;
20import com.android.ide.common.rendering.api.ResourceValue;
21import com.android.ide.common.rendering.api.StyleResourceValue;
22import com.android.layoutlib.bridge.Bridge;
23import com.android.layoutlib.bridge.BridgeConstants;
24import com.android.layoutlib.bridge.impl.Stack;
25
26import android.app.Activity;
27import android.app.Fragment;
28import android.content.BroadcastReceiver;
29import android.content.ComponentName;
30import android.content.ContentResolver;
31import android.content.Context;
32import android.content.Intent;
33import android.content.IntentFilter;
34import android.content.IntentSender;
35import android.content.ServiceConnection;
36import android.content.SharedPreferences;
37import android.content.pm.ApplicationInfo;
38import android.content.pm.PackageManager;
39import android.content.res.AssetManager;
40import android.content.res.Configuration;
41import android.content.res.Resources;
42import android.content.res.TypedArray;
43import android.content.res.Resources.Theme;
44import android.database.DatabaseErrorHandler;
45import android.database.sqlite.SQLiteDatabase;
46import android.database.sqlite.SQLiteDatabase.CursorFactory;
47import android.graphics.Bitmap;
48import android.graphics.drawable.Drawable;
49import android.net.Uri;
50import android.os.Bundle;
51import android.os.Handler;
52import android.os.Looper;
53import android.util.AttributeSet;
54import android.util.DisplayMetrics;
55import android.view.LayoutInflater;
56import android.view.View;
57
58import java.io.File;
59import java.io.FileInputStream;
60import java.io.FileNotFoundException;
61import java.io.FileOutputStream;
62import java.io.IOException;
63import java.io.InputStream;
64import java.util.HashMap;
65import java.util.IdentityHashMap;
66import java.util.Map;
67import java.util.TreeMap;
68import java.util.Map.Entry;
69
70/**
71 * Custom implementation of Context/Activity to handle non compiled resources.
72 */
73public final class BridgeContext extends Activity {
74
75    private Resources mResources;
76    private Theme mTheme;
77    private final HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>();
78    private final StyleResourceValue mThemeValues;
79    private final Object mProjectKey;
80    private final DisplayMetrics mMetrics;
81    private final Map<String, Map<String, ResourceValue>> mProjectResources;
82    private final Map<String, Map<String, ResourceValue>> mFrameworkResources;
83    private final Map<StyleResourceValue, StyleResourceValue> mStyleInheritanceMap;
84
85    private final Map<Object, Map<String, String>> mDefaultPropMaps =
86        new IdentityHashMap<Object, Map<String,String>>();
87
88    // maps for dynamically generated id representing style objects (IStyleResourceValue)
89    private Map<Integer, StyleResourceValue> mDynamicIdToStyleMap;
90    private Map<StyleResourceValue, Integer> mStyleToDynamicIdMap;
91    private int mDynamicIdGenerator = 0x01030000; // Base id for framework R.style
92
93    // cache for TypedArray generated from IStyleResourceValue object
94    private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache;
95    private BridgeInflater mBridgeInflater;
96
97    private final IProjectCallback mProjectCallback;
98    private BridgeContentResolver mContentResolver;
99
100    private final Stack<BridgeXmlBlockParser> mParserStack = new Stack<BridgeXmlBlockParser>();
101
102    /**
103     * @param projectKey An Object identifying the project. This is used for the cache mechanism.
104     * @param metrics the {@link DisplayMetrics}.
105     * @param themeName The name of the theme to use.
106     * @param projectResources the resources of the project. The map contains (String, map) pairs
107     * where the string is the type of the resource reference used in the layout file, and the
108     * map contains (String, {@link }) pairs where the key is the resource name,
109     * and the value is the resource value.
110     * @param frameworkResources the framework resources. The map contains (String, map) pairs
111     * where the string is the type of the resource reference used in the layout file, and the map
112     * contains (String, {@link ResourceValue}) pairs where the key is the resource name, and the
113     * value is the resource value.
114     * @param styleInheritanceMap
115     * @param projectCallback
116     */
117    public BridgeContext(Object projectKey, DisplayMetrics metrics,
118            StyleResourceValue currentTheme,
119            Map<String, Map<String, ResourceValue>> projectResources,
120            Map<String, Map<String, ResourceValue>> frameworkResources,
121            Map<StyleResourceValue, StyleResourceValue> styleInheritanceMap,
122            IProjectCallback projectCallback) {
123        mProjectKey = projectKey;
124        mMetrics = metrics;
125        mProjectCallback = projectCallback;
126
127        mThemeValues = currentTheme;
128        mProjectResources = projectResources;
129        mFrameworkResources = frameworkResources;
130        mStyleInheritanceMap = styleInheritanceMap;
131
132        mFragments.mCurState = Fragment.CREATED;
133        mFragments.mActivity = this;
134    }
135
136    /**
137     * Initializes the {@link Resources} singleton to be linked to this {@link Context}, its
138     * {@link DisplayMetrics}, {@link Configuration}, and {@link IProjectCallback}.
139     *
140     * @see #disposeResources()
141     */
142    public void initResources() {
143        AssetManager assetManager = AssetManager.getSystem();
144        Configuration config = new Configuration();
145
146        mResources = BridgeResources.initSystem(
147                this,
148                assetManager,
149                mMetrics,
150                config,
151                mProjectCallback);
152        mTheme = mResources.newTheme();
153    }
154
155    /**
156     * Disposes the {@link Resources} singleton.
157     */
158    public void disposeResources() {
159        BridgeResources.disposeSystem();
160    }
161
162    public void setBridgeInflater(BridgeInflater inflater) {
163        mBridgeInflater = inflater;
164    }
165
166    public void addViewKey(View view, Object viewKey) {
167        mViewKeyMap.put(view, viewKey);
168    }
169
170    public Object getViewKey(View view) {
171        return mViewKeyMap.get(view);
172    }
173
174    public Object getProjectKey() {
175        return mProjectKey;
176    }
177
178    public IProjectCallback getProjectCallback() {
179        return mProjectCallback;
180    }
181
182    public Map<String, String> getDefaultPropMap(Object key) {
183        return mDefaultPropMaps.get(key);
184    }
185
186    /**
187     * Adds a parser to the stack.
188     * @param parser the parser to add.
189     */
190    public void pushParser(BridgeXmlBlockParser parser) {
191        mParserStack.push(parser);
192    }
193
194    /**
195     * Removes the parser at the top of the stack
196     */
197    public void popParser() {
198        mParserStack.pop();
199    }
200
201    /**
202     * Returns the current parser at the top the of the stack.
203     * @return a parser or null.
204     */
205    public BridgeXmlBlockParser getCurrentParser() {
206        return mParserStack.peek();
207    }
208
209    /**
210     * Returns the previous parser.
211     * @return a parser or null if there isn't any previous parser
212     */
213    public BridgeXmlBlockParser getPreviousParser() {
214        if (mParserStack.size() < 2) {
215            return null;
216        }
217        return mParserStack.get(mParserStack.size() - 2);
218    }
219
220    // ------------- Activity Methods
221
222    @Override
223    public LayoutInflater getLayoutInflater() {
224        return mBridgeInflater;
225    }
226
227    // ------------ Context methods
228
229    @Override
230    public Resources getResources() {
231        return mResources;
232    }
233
234    @Override
235    public Theme getTheme() {
236        return mTheme;
237    }
238
239    @Override
240    public ClassLoader getClassLoader() {
241        return this.getClass().getClassLoader();
242    }
243
244    @Override
245    public Object getSystemService(String service) {
246        if (LAYOUT_INFLATER_SERVICE.equals(service)) {
247            return mBridgeInflater;
248        }
249
250        // AutoCompleteTextView and MultiAutoCompleteTextView want a window
251        // service. We don't have any but it's not worth an exception.
252        if (WINDOW_SERVICE.equals(service)) {
253            return null;
254        }
255
256        throw new UnsupportedOperationException("Unsupported Service: " + service);
257    }
258
259
260    @Override
261    public final TypedArray obtainStyledAttributes(int[] attrs) {
262        return createStyleBasedTypedArray(mThemeValues, attrs);
263    }
264
265    @Override
266    public final TypedArray obtainStyledAttributes(int resid, int[] attrs)
267            throws Resources.NotFoundException {
268        // get the IStyleResourceValue based on the resId;
269        StyleResourceValue style = getStyleByDynamicId(resid);
270
271        if (style == null) {
272            throw new Resources.NotFoundException();
273        }
274
275        if (mTypedArrayCache == null) {
276            mTypedArrayCache = new HashMap<int[], Map<Integer,TypedArray>>();
277
278            Map<Integer, TypedArray> map = new HashMap<Integer, TypedArray>();
279            mTypedArrayCache.put(attrs, map);
280
281            BridgeTypedArray ta = createStyleBasedTypedArray(style, attrs);
282            map.put(resid, ta);
283
284            return ta;
285        }
286
287        // get the 2nd map
288        Map<Integer, TypedArray> map = mTypedArrayCache.get(attrs);
289        if (map == null) {
290            map = new HashMap<Integer, TypedArray>();
291            mTypedArrayCache.put(attrs, map);
292        }
293
294        // get the array from the 2nd map
295        TypedArray ta = map.get(resid);
296
297        if (ta == null) {
298            ta = createStyleBasedTypedArray(style, attrs);
299            map.put(resid, ta);
300        }
301
302        return ta;
303    }
304
305    @Override
306    public final TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) {
307        return obtainStyledAttributes(set, attrs, 0, 0);
308    }
309
310    @Override
311    public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs,
312            int defStyleAttr, int defStyleRes) {
313
314        Map<String, String> defaultPropMap = null;
315        boolean isPlatformFile = true;
316
317        // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java
318        if (set instanceof BridgeXmlBlockParser) {
319            BridgeXmlBlockParser parser = null;
320            parser = (BridgeXmlBlockParser)set;
321
322            isPlatformFile = parser.isPlatformFile();
323
324            Object key = parser.getViewCookie();
325            if (key != null) {
326                defaultPropMap = mDefaultPropMaps.get(key);
327                if (defaultPropMap == null) {
328                    defaultPropMap = new HashMap<String, String>();
329                    mDefaultPropMaps.put(key, defaultPropMap);
330                }
331            }
332
333        } else if (set instanceof BridgeLayoutParamsMapAttributes) {
334            // this is only for temp layout params generated dynamically, so this is never
335            // platform content.
336            isPlatformFile = false;
337        } else if (set != null) { // null parser is ok
338            // really this should not be happening since its instantiated in Bridge
339            Bridge.getLog().error(null, "Parser is not a BridgeXmlBlockParser!");
340            return null;
341        }
342
343        boolean[] frameworkAttributes = new boolean[1];
344        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, frameworkAttributes);
345
346        BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length,
347                isPlatformFile);
348
349        // resolve the defStyleAttr value into a IStyleResourceValue
350        StyleResourceValue defStyleValues = null;
351
352        // look for a custom style.
353        String customStyle = null;
354        if (set != null) {
355            customStyle = set.getAttributeValue(null /* namespace*/, "style");
356        }
357        if (customStyle != null) {
358            ResourceValue item = findResValue(customStyle, false /*forceFrameworkOnly*/);
359
360            if (item instanceof StyleResourceValue) {
361                defStyleValues = (StyleResourceValue)item;
362            }
363        }
364
365        if (defStyleValues == null && defStyleAttr != 0) {
366            // get the name from the int.
367            String defStyleName = searchAttr(defStyleAttr);
368
369            if (defaultPropMap != null) {
370                defaultPropMap.put("style", defStyleName);
371            }
372
373            // look for the style in the current theme, and its parent:
374            if (mThemeValues != null) {
375                ResourceValue item = findItemInStyle(mThemeValues, defStyleName);
376
377                if (item != null) {
378                    // item is a reference to a style entry. Search for it.
379                    item = findResValue(item.getValue(), false /*forceFrameworkOnly*/);
380
381                    if (item instanceof StyleResourceValue) {
382                        defStyleValues = (StyleResourceValue)item;
383                    }
384                } else {
385                    Bridge.getLog().error(null,
386                            String.format(
387                                    "Failed to find style '%s' in current theme", defStyleName));
388                }
389            }
390        }
391
392        if (defStyleRes != 0) {
393            // FIXME: See what we need to do with this.
394            throw new UnsupportedOperationException();
395        }
396
397        String namespace = BridgeConstants.NS_RESOURCES;
398        if (frameworkAttributes[0] == false) {
399            // need to use the application namespace
400            namespace = mProjectCallback.getNamespace();
401        }
402
403        if (styleNameMap != null) {
404            for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
405                int index = styleAttribute.getKey().intValue();
406
407                String name = styleAttribute.getValue();
408                String value = null;
409                if (set != null) {
410                    value = set.getAttributeValue(namespace, name);
411                }
412
413                // if there's no direct value for this attribute in the XML, we look for default
414                // values in the widget defStyle, and then in the theme.
415                if (value == null) {
416                    ResourceValue resValue = null;
417
418                    // look for the value in the defStyle first (and its parent if needed)
419                    if (defStyleValues != null) {
420                        resValue = findItemInStyle(defStyleValues, name);
421                    }
422
423                    // if the item is not present in the defStyle, we look in the main theme (and
424                    // its parent themes)
425                    if (resValue == null && mThemeValues != null) {
426                        resValue = findItemInStyle(mThemeValues, name);
427                    }
428
429                    // if we found a value, we make sure this doesn't reference another value.
430                    // So we resolve it.
431                    if (resValue != null) {
432                        // put the first default value, before the resolution.
433                        if (defaultPropMap != null) {
434                            defaultPropMap.put(name, resValue.getValue());
435                        }
436
437                        resValue = resolveResValue(resValue);
438                    }
439
440                    ta.bridgeSetValue(index, name, resValue);
441                } else {
442                    // there is a value in the XML, but we need to resolve it in case it's
443                    // referencing another resource or a theme value.
444                    ta.bridgeSetValue(index, name, resolveValue(null, name, value));
445                }
446            }
447        }
448
449        ta.sealArray();
450
451        return ta;
452    }
453
454    @Override
455    public Looper getMainLooper() {
456        return Looper.myLooper();
457    }
458
459
460    // ------------- private new methods
461
462    /**
463     * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the
464     * values found in the given style.
465     * @see #obtainStyledAttributes(int, int[])
466     */
467    private BridgeTypedArray createStyleBasedTypedArray(StyleResourceValue style, int[] attrs)
468            throws Resources.NotFoundException {
469        TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, null);
470
471        BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length,
472                false /* platformResourceFlag */);
473
474        // loop through all the values in the style map, and init the TypedArray with
475        // the style we got from the dynamic id
476        for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) {
477            int index = styleAttribute.getKey().intValue();
478
479            String name = styleAttribute.getValue();
480
481            // get the value from the style, or its parent styles.
482            ResourceValue resValue = findItemInStyle(style, name);
483
484            // resolve it to make sure there are no references left.
485            ta.bridgeSetValue(index, name, resolveResValue(resValue));
486        }
487
488        ta.sealArray();
489
490        return ta;
491    }
492
493
494    /**
495     * Resolves the value of a resource, if the value references a theme or resource value.
496     * <p/>
497     * This method ensures that it returns a {@link ResourceValue} object that does not
498     * reference another resource.
499     * If the resource cannot be resolved, it returns <code>null</code>.
500     * <p/>
501     * If a value that does not need to be resolved is given, the method will return a new
502     * instance of {@link ResourceValue} that contains the input value.
503     *
504     * @param type the type of the resource
505     * @param name the name of the attribute containing this value.
506     * @param value the resource value, or reference to resolve
507     * @return the resolved resource value or <code>null</code> if it failed to resolve it.
508     */
509    private ResourceValue resolveValue(String type, String name, String value) {
510        if (value == null) {
511            return null;
512        }
513
514        // get the ResourceValue referenced by this value
515        ResourceValue resValue = findResValue(value, false /*forceFrameworkOnly*/);
516
517        // if resValue is null, but value is not null, this means it was not a reference.
518        // we return the name/value wrapper in a ResourceValue. the isFramework flag doesn't
519        // matter.
520        if (resValue == null) {
521            return new ResourceValue(type, name, value, false /*isFramework*/);
522        }
523
524        // we resolved a first reference, but we need to make sure this isn't a reference also.
525        return resolveResValue(resValue);
526    }
527
528    /**
529     * Returns the {@link ResourceValue} referenced by the value of <var>value</var>.
530     * <p/>
531     * This method ensures that it returns a {@link ResourceValue} object that does not
532     * reference another resource.
533     * If the resource cannot be resolved, it returns <code>null</code>.
534     * <p/>
535     * If a value that does not need to be resolved is given, the method will return the input
536     * value.
537     *
538     * @param value the value containing the reference to resolve.
539     * @return a {@link ResourceValue} object or <code>null</code>
540     */
541    public ResourceValue resolveResValue(ResourceValue value) {
542        if (value == null) {
543            return null;
544        }
545
546        // if the resource value is a style, we simply return it.
547        if (value instanceof StyleResourceValue) {
548            return value;
549        }
550
551        // else attempt to find another ResourceValue referenced by this one.
552        ResourceValue resolvedValue = findResValue(value.getValue(), value.isFramework());
553
554        // if the value did not reference anything, then we simply return the input value
555        if (resolvedValue == null) {
556            return value;
557        }
558
559        // otherwise, we attempt to resolve this new value as well
560        return resolveResValue(resolvedValue);
561    }
562
563    /**
564     * Searches for, and returns a {@link ResourceValue} by its reference.
565     * <p/>
566     * The reference format can be:
567     * <pre>@resType/resName</pre>
568     * <pre>@android:resType/resName</pre>
569     * <pre>@resType/android:resName</pre>
570     * <pre>?resType/resName</pre>
571     * <pre>?android:resType/resName</pre>
572     * <pre>?resType/android:resName</pre>
573     * Any other string format will return <code>null</code>.
574     * <p/>
575     * The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method
576     * only support the android namespace.
577     *
578     * @param reference the resource reference to search for.
579     * @param forceFrameworkOnly if true all references are considered to be toward framework
580     *      resource even if the reference does not include the android: prefix.
581     * @return a {@link ResourceValue} or <code>null</code>.
582     */
583    ResourceValue findResValue(String reference, boolean forceFrameworkOnly) {
584        if (reference == null) {
585            return null;
586        }
587        if (reference.startsWith(BridgeConstants.PREFIX_THEME_REF)) {
588            // no theme? no need to go further!
589            if (mThemeValues == null) {
590                return null;
591            }
592
593            boolean frameworkOnly = false;
594
595            // eliminate the prefix from the string
596            if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_THEME_REF)) {
597                frameworkOnly = true;
598                reference = reference.substring(BridgeConstants.PREFIX_ANDROID_THEME_REF.length());
599            } else {
600                reference = reference.substring(BridgeConstants.PREFIX_THEME_REF.length());
601            }
602
603            // at this point, value can contain type/name (drawable/foo for instance).
604            // split it to make sure.
605            String[] segments = reference.split("\\/");
606
607            // we look for the referenced item name.
608            String referenceName = null;
609
610            if (segments.length == 2) {
611                // there was a resType in the reference. If it's attr, we ignore it
612                // else, we assert for now.
613                if (BridgeConstants.RES_ATTR.equals(segments[0])) {
614                    referenceName = segments[1];
615                } else {
616                    // At this time, no support for ?type/name where type is not "attr"
617                    return null;
618                }
619            } else {
620                // it's just an item name.
621                referenceName = segments[0];
622            }
623
624            // now we look for android: in the referenceName in order to support format
625            // such as: ?attr/android:name
626            if (referenceName.startsWith(BridgeConstants.PREFIX_ANDROID)) {
627                frameworkOnly = true;
628                referenceName = referenceName.substring(BridgeConstants.PREFIX_ANDROID.length());
629            }
630
631            // Now look for the item in the theme, starting with the current one.
632            if (frameworkOnly) {
633                // FIXME for now we do the same as if it didn't specify android:
634                return findItemInStyle(mThemeValues, referenceName);
635            }
636
637            return findItemInStyle(mThemeValues, referenceName);
638        } else if (reference.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) {
639            boolean frameworkOnly = false;
640
641            // check for the specific null reference value.
642            if (BridgeConstants.REFERENCE_NULL.equals(reference)) {
643                return null;
644            }
645
646            // Eliminate the prefix from the string.
647            if (reference.startsWith(BridgeConstants.PREFIX_ANDROID_RESOURCE_REF)) {
648                frameworkOnly = true;
649                reference = reference.substring(
650                        BridgeConstants.PREFIX_ANDROID_RESOURCE_REF.length());
651            } else {
652                reference = reference.substring(BridgeConstants.PREFIX_RESOURCE_REF.length());
653            }
654
655            // at this point, value contains type/[android:]name (drawable/foo for instance)
656            String[] segments = reference.split("\\/");
657
658            // now we look for android: in the resource name in order to support format
659            // such as: @drawable/android:name
660            if (segments[1].startsWith(BridgeConstants.PREFIX_ANDROID)) {
661                frameworkOnly = true;
662                segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length());
663            }
664
665            return findResValue(segments[0], segments[1],
666                    forceFrameworkOnly ? true :frameworkOnly);
667        }
668
669        // Looks like the value didn't reference anything. Return null.
670        return null;
671    }
672
673    /**
674     * Searches for, and returns a {@link ResourceValue} by its name, and type.
675     * @param resType the type of the resource
676     * @param resName  the name of the resource
677     * @param frameworkOnly if <code>true</code>, the method does not search in the
678     * project resources
679     */
680    private ResourceValue findResValue(String resType, String resName, boolean frameworkOnly) {
681        // map of ResouceValue for the given type
682        Map<String, ResourceValue> typeMap;
683
684        // if allowed, search in the project resources first.
685        if (frameworkOnly == false) {
686            typeMap = mProjectResources.get(resType);
687            if (typeMap != null) {
688                ResourceValue item = typeMap.get(resName);
689                if (item != null) {
690                    return item;
691                }
692            }
693        }
694
695        // now search in the framework resources.
696        typeMap = mFrameworkResources.get(resType);
697        if (typeMap != null) {
698            ResourceValue item = typeMap.get(resName);
699            if (item != null) {
700                return item;
701            }
702        }
703
704        // didn't find the resource anywhere.
705        // This is normal if the resource is an ID that is generated automatically.
706        // For other resources, we output a warning
707        if ("+id".equals(resType) == false && "+android:id".equals(resType) == false) { //$NON-NLS-1$ //$NON-NLS-2$
708            Bridge.getLog().warning("resources", //$NON-NLS-1$
709                    "Couldn't resolve resource @" +
710                    (frameworkOnly ? "android:" : "") + resType + "/" + resName);
711        }
712        return null;
713    }
714
715    /**
716     * Returns a framework resource by type and name. The returned resource is resolved.
717     * @param resourceType the type of the resource
718     * @param resourceName the name of the resource
719     */
720    public ResourceValue getFrameworkResource(String resourceType, String resourceName) {
721        return getResource(resourceType, resourceName, mFrameworkResources);
722    }
723
724    /**
725     * Returns a project resource by type and name. The returned resource is resolved.
726     * @param resourceType the type of the resource
727     * @param resourceName the name of the resource
728     */
729    public ResourceValue getProjectResource(String resourceType, String resourceName) {
730        return getResource(resourceType, resourceName, mProjectResources);
731    }
732
733    ResourceValue getResource(String resourceType, String resourceName,
734            Map<String, Map<String, ResourceValue>> resourceRepository) {
735        Map<String, ResourceValue> typeMap = resourceRepository.get(resourceType);
736        if (typeMap != null) {
737            ResourceValue item = typeMap.get(resourceName);
738            if (item != null) {
739                item = resolveResValue(item);
740                return item;
741            }
742        }
743
744        // didn't find the resource anywhere.
745        return null;
746
747    }
748
749    /**
750     * Returns the {@link ResourceValue} matching a given name in a given style. If the
751     * item is not directly available in the style, the method looks in its parent style.
752     * @param style the style to search in
753     * @param itemName the name of the item to search for.
754     * @return the {@link ResourceValue} object or <code>null</code>
755     */
756    public ResourceValue findItemInStyle(StyleResourceValue style, String itemName) {
757        ResourceValue item = style.findValue(itemName);
758
759        // if we didn't find it, we look in the parent style (if applicable)
760        if (item == null && mStyleInheritanceMap != null) {
761            StyleResourceValue parentStyle = mStyleInheritanceMap.get(style);
762            if (parentStyle != null) {
763                return findItemInStyle(parentStyle, itemName);
764            }
765        }
766
767        return item;
768    }
769
770    /**
771     * The input int[] attrs is one of com.android.internal.R.styleable fields where the name
772     * of the field is the style being referenced and the array contains one index per attribute.
773     * <p/>
774     * searchAttrs() finds all the names of the attributes referenced so for example if
775     * attrs == com.android.internal.R.styleable.View, this returns the list of the "xyz" where
776     * there's a field com.android.internal.R.styleable.View_xyz and the field value is the index
777     * that is used to reference the attribute later in the TypedArray.
778     *
779     * @param attrs An attribute array reference given to obtainStyledAttributes.
780     * @return A sorted map Attribute-Value to Attribute-Name for all attributes declared by the
781     *         attribute array. Returns null if nothing is found.
782     */
783    private TreeMap<Integer,String> searchAttrs(int[] attrs, boolean[] outFrameworkFlag) {
784        // get the name of the array from the framework resources
785        String arrayName = Bridge.resolveResourceValue(attrs);
786        if (arrayName != null) {
787            // if we found it, get the name of each of the int in the array.
788            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
789            for (int i = 0 ; i < attrs.length ; i++) {
790                String[] info = Bridge.resolveResourceValue(attrs[i]);
791                if (info != null) {
792                    attributes.put(i, info[0]);
793                } else {
794                    // FIXME Not sure what we should be doing here...
795                    attributes.put(i, null);
796                }
797            }
798
799            if (outFrameworkFlag != null) {
800                outFrameworkFlag[0] = true;
801            }
802
803            return attributes;
804        }
805
806        // if the name was not found in the framework resources, look in the project
807        // resources
808        arrayName = mProjectCallback.resolveResourceValue(attrs);
809        if (arrayName != null) {
810            TreeMap<Integer,String> attributes = new TreeMap<Integer, String>();
811            for (int i = 0 ; i < attrs.length ; i++) {
812                String[] info = mProjectCallback.resolveResourceValue(attrs[i]);
813                if (info != null) {
814                    attributes.put(i, info[0]);
815                } else {
816                    // FIXME Not sure what we should be doing here...
817                    attributes.put(i, null);
818                }
819            }
820
821            if (outFrameworkFlag != null) {
822                outFrameworkFlag[0] = false;
823            }
824
825            return attributes;
826        }
827
828        return null;
829    }
830
831    /**
832     * Searches for the attribute referenced by its internal id.
833     *
834     * @param attr An attribute reference given to obtainStyledAttributes such as defStyle.
835     * @return The unique name of the attribute, if found, e.g. "buttonStyle". Returns null
836     *         if nothing is found.
837     */
838    public String searchAttr(int attr) {
839        String[] info = Bridge.resolveResourceValue(attr);
840        if (info != null) {
841            return info[0];
842        }
843
844        info = mProjectCallback.resolveResourceValue(attr);
845        if (info != null) {
846            return info[0];
847        }
848
849        return null;
850    }
851
852    int getDynamicIdByStyle(StyleResourceValue resValue) {
853        if (mDynamicIdToStyleMap == null) {
854            // create the maps.
855            mDynamicIdToStyleMap = new HashMap<Integer, StyleResourceValue>();
856            mStyleToDynamicIdMap = new HashMap<StyleResourceValue, Integer>();
857        }
858
859        // look for an existing id
860        Integer id = mStyleToDynamicIdMap.get(resValue);
861
862        if (id == null) {
863            // generate a new id
864            id = Integer.valueOf(++mDynamicIdGenerator);
865
866            // and add it to the maps.
867            mDynamicIdToStyleMap.put(id, resValue);
868            mStyleToDynamicIdMap.put(resValue, id);
869        }
870
871        return id;
872    }
873
874    private StyleResourceValue getStyleByDynamicId(int i) {
875        if (mDynamicIdToStyleMap != null) {
876            return mDynamicIdToStyleMap.get(i);
877        }
878
879        return null;
880    }
881
882    int getFrameworkResourceValue(String resType, String resName, int defValue) {
883        Integer value = Bridge.getResourceValue(resType, resName);
884        if (value != null) {
885            return value.intValue();
886        }
887
888        return defValue;
889    }
890
891    int getProjectResourceValue(String resType, String resName, int defValue) {
892        if (mProjectCallback != null) {
893            Integer value = mProjectCallback.getResourceValue(resType, resName);
894            if (value != null) {
895                return value.intValue();
896            }
897        }
898
899        return defValue;
900    }
901
902    //------------ NOT OVERRIDEN --------------------
903
904    @Override
905    public boolean bindService(Intent arg0, ServiceConnection arg1, int arg2) {
906        // TODO Auto-generated method stub
907        return false;
908    }
909
910    @Override
911    public int checkCallingOrSelfPermission(String arg0) {
912        // TODO Auto-generated method stub
913        return 0;
914    }
915
916    @Override
917    public int checkCallingOrSelfUriPermission(Uri arg0, int arg1) {
918        // TODO Auto-generated method stub
919        return 0;
920    }
921
922    @Override
923    public int checkCallingPermission(String arg0) {
924        // TODO Auto-generated method stub
925        return 0;
926    }
927
928    @Override
929    public int checkCallingUriPermission(Uri arg0, int arg1) {
930        // TODO Auto-generated method stub
931        return 0;
932    }
933
934    @Override
935    public int checkPermission(String arg0, int arg1, int arg2) {
936        // TODO Auto-generated method stub
937        return 0;
938    }
939
940    @Override
941    public int checkUriPermission(Uri arg0, int arg1, int arg2, int arg3) {
942        // TODO Auto-generated method stub
943        return 0;
944    }
945
946    @Override
947    public int checkUriPermission(Uri arg0, String arg1, String arg2, int arg3,
948            int arg4, int arg5) {
949        // TODO Auto-generated method stub
950        return 0;
951    }
952
953    @Override
954    public void clearWallpaper() {
955        // TODO Auto-generated method stub
956
957    }
958
959    @Override
960    public Context createPackageContext(String arg0, int arg1) {
961        // TODO Auto-generated method stub
962        return null;
963    }
964
965    @Override
966    public String[] databaseList() {
967        // TODO Auto-generated method stub
968        return null;
969    }
970
971    @Override
972    public boolean deleteDatabase(String arg0) {
973        // TODO Auto-generated method stub
974        return false;
975    }
976
977    @Override
978    public boolean deleteFile(String arg0) {
979        // TODO Auto-generated method stub
980        return false;
981    }
982
983    @Override
984    public void enforceCallingOrSelfPermission(String arg0, String arg1) {
985        // TODO Auto-generated method stub
986
987    }
988
989    @Override
990    public void enforceCallingOrSelfUriPermission(Uri arg0, int arg1,
991            String arg2) {
992        // TODO Auto-generated method stub
993
994    }
995
996    @Override
997    public void enforceCallingPermission(String arg0, String arg1) {
998        // TODO Auto-generated method stub
999
1000    }
1001
1002    @Override
1003    public void enforceCallingUriPermission(Uri arg0, int arg1, String arg2) {
1004        // TODO Auto-generated method stub
1005
1006    }
1007
1008    @Override
1009    public void enforcePermission(String arg0, int arg1, int arg2, String arg3) {
1010        // TODO Auto-generated method stub
1011
1012    }
1013
1014    @Override
1015    public void enforceUriPermission(Uri arg0, int arg1, int arg2, int arg3,
1016            String arg4) {
1017        // TODO Auto-generated method stub
1018
1019    }
1020
1021    @Override
1022    public void enforceUriPermission(Uri arg0, String arg1, String arg2,
1023            int arg3, int arg4, int arg5, String arg6) {
1024        // TODO Auto-generated method stub
1025
1026    }
1027
1028    @Override
1029    public String[] fileList() {
1030        // TODO Auto-generated method stub
1031        return null;
1032    }
1033
1034    @Override
1035    public AssetManager getAssets() {
1036        // TODO Auto-generated method stub
1037        return null;
1038    }
1039
1040    @Override
1041    public File getCacheDir() {
1042        // TODO Auto-generated method stub
1043        return null;
1044    }
1045
1046    @Override
1047    public File getExternalCacheDir() {
1048        // TODO Auto-generated method stub
1049        return null;
1050    }
1051
1052    @Override
1053    public ContentResolver getContentResolver() {
1054        if (mContentResolver == null) {
1055            mContentResolver = new BridgeContentResolver(this);
1056        }
1057        return mContentResolver;
1058    }
1059
1060    @Override
1061    public File getDatabasePath(String arg0) {
1062        // TODO Auto-generated method stub
1063        return null;
1064    }
1065
1066    @Override
1067    public File getDir(String arg0, int arg1) {
1068        // TODO Auto-generated method stub
1069        return null;
1070    }
1071
1072    @Override
1073    public File getFileStreamPath(String arg0) {
1074        // TODO Auto-generated method stub
1075        return null;
1076    }
1077
1078    @Override
1079    public File getFilesDir() {
1080        // TODO Auto-generated method stub
1081        return null;
1082    }
1083
1084    @Override
1085    public File getExternalFilesDir(String type) {
1086        // TODO Auto-generated method stub
1087        return null;
1088    }
1089
1090    @Override
1091    public String getPackageCodePath() {
1092        // TODO Auto-generated method stub
1093        return null;
1094    }
1095
1096    @Override
1097    public PackageManager getPackageManager() {
1098        // TODO Auto-generated method stub
1099        return null;
1100    }
1101
1102    @Override
1103    public String getPackageName() {
1104        // TODO Auto-generated method stub
1105        return null;
1106    }
1107
1108    @Override
1109    public ApplicationInfo getApplicationInfo() {
1110        return new ApplicationInfo();
1111    }
1112
1113    @Override
1114    public String getPackageResourcePath() {
1115        // TODO Auto-generated method stub
1116        return null;
1117    }
1118
1119    @Override
1120    public File getSharedPrefsFile(String name) {
1121        // TODO Auto-generated method stub
1122        return null;
1123    }
1124
1125    @Override
1126    public SharedPreferences getSharedPreferences(String arg0, int arg1) {
1127        // TODO Auto-generated method stub
1128        return null;
1129    }
1130
1131    @Override
1132    public Drawable getWallpaper() {
1133        // TODO Auto-generated method stub
1134        return null;
1135    }
1136
1137    @Override
1138    public int getWallpaperDesiredMinimumWidth() {
1139        return -1;
1140    }
1141
1142    @Override
1143    public int getWallpaperDesiredMinimumHeight() {
1144        return -1;
1145    }
1146
1147    @Override
1148    public void grantUriPermission(String arg0, Uri arg1, int arg2) {
1149        // TODO Auto-generated method stub
1150
1151    }
1152
1153    @Override
1154    public FileInputStream openFileInput(String arg0) throws FileNotFoundException {
1155        // TODO Auto-generated method stub
1156        return null;
1157    }
1158
1159    @Override
1160    public FileOutputStream openFileOutput(String arg0, int arg1) throws FileNotFoundException {
1161        // TODO Auto-generated method stub
1162        return null;
1163    }
1164
1165    @Override
1166    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1, CursorFactory arg2) {
1167        // TODO Auto-generated method stub
1168        return null;
1169    }
1170
1171    @Override
1172    public SQLiteDatabase openOrCreateDatabase(String arg0, int arg1,
1173            CursorFactory arg2, DatabaseErrorHandler arg3) {
1174        // TODO Auto-generated method stub
1175        return null;
1176    }
1177
1178    @Override
1179    public Drawable peekWallpaper() {
1180        // TODO Auto-generated method stub
1181        return null;
1182    }
1183
1184    @Override
1185    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1) {
1186        // TODO Auto-generated method stub
1187        return null;
1188    }
1189
1190    @Override
1191    public Intent registerReceiver(BroadcastReceiver arg0, IntentFilter arg1,
1192            String arg2, Handler arg3) {
1193        // TODO Auto-generated method stub
1194        return null;
1195    }
1196
1197    @Override
1198    public void removeStickyBroadcast(Intent arg0) {
1199        // TODO Auto-generated method stub
1200
1201    }
1202
1203    @Override
1204    public void revokeUriPermission(Uri arg0, int arg1) {
1205        // TODO Auto-generated method stub
1206
1207    }
1208
1209    @Override
1210    public void sendBroadcast(Intent arg0) {
1211        // TODO Auto-generated method stub
1212
1213    }
1214
1215    @Override
1216    public void sendBroadcast(Intent arg0, String arg1) {
1217        // TODO Auto-generated method stub
1218
1219    }
1220
1221    @Override
1222    public void sendOrderedBroadcast(Intent arg0, String arg1) {
1223        // TODO Auto-generated method stub
1224
1225    }
1226
1227    @Override
1228    public void sendOrderedBroadcast(Intent arg0, String arg1,
1229            BroadcastReceiver arg2, Handler arg3, int arg4, String arg5,
1230            Bundle arg6) {
1231        // TODO Auto-generated method stub
1232
1233    }
1234
1235    @Override
1236    public void sendStickyBroadcast(Intent arg0) {
1237        // TODO Auto-generated method stub
1238
1239    }
1240
1241    @Override
1242    public void sendStickyOrderedBroadcast(Intent intent,
1243            BroadcastReceiver resultReceiver, Handler scheduler, int initialCode, String initialData,
1244           Bundle initialExtras) {
1245        // TODO Auto-generated method stub
1246    }
1247
1248    @Override
1249    public void setTheme(int arg0) {
1250        // TODO Auto-generated method stub
1251
1252    }
1253
1254    @Override
1255    public void setWallpaper(Bitmap arg0) throws IOException {
1256        // TODO Auto-generated method stub
1257
1258    }
1259
1260    @Override
1261    public void setWallpaper(InputStream arg0) throws IOException {
1262        // TODO Auto-generated method stub
1263
1264    }
1265
1266    @Override
1267    public void startActivity(Intent arg0) {
1268        // TODO Auto-generated method stub
1269
1270    }
1271
1272    @Override
1273    public void startIntentSender(IntentSender intent,
1274            Intent fillInIntent, int flagsMask, int flagsValues, int extraFlags)
1275            throws IntentSender.SendIntentException {
1276        // TODO Auto-generated method stub
1277    }
1278
1279    @Override
1280    public boolean startInstrumentation(ComponentName arg0, String arg1,
1281            Bundle arg2) {
1282        // TODO Auto-generated method stub
1283        return false;
1284    }
1285
1286    @Override
1287    public ComponentName startService(Intent arg0) {
1288        // TODO Auto-generated method stub
1289        return null;
1290    }
1291
1292    @Override
1293    public boolean stopService(Intent arg0) {
1294        // TODO Auto-generated method stub
1295        return false;
1296    }
1297
1298    @Override
1299    public void unbindService(ServiceConnection arg0) {
1300        // TODO Auto-generated method stub
1301
1302    }
1303
1304    @Override
1305    public void unregisterReceiver(BroadcastReceiver arg0) {
1306        // TODO Auto-generated method stub
1307
1308    }
1309
1310    @Override
1311    public Context getApplicationContext() {
1312        throw new UnsupportedOperationException();
1313    }
1314
1315    @Override
1316    public void startActivities(Intent[] arg0) {
1317        // TODO Auto-generated method stub
1318
1319    }
1320
1321    @Override
1322    public boolean isRestricted() {
1323        return false;
1324    }
1325}
1326