OSDN Git Service

Remove stale temporary ASEC containers
[android-x86/frameworks-base.git] / tools / layoutlib / bridge / src / com / android / layoutlib / bridge / android / BridgeResources.java
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
17 package com.android.layoutlib.bridge.android;
18
19 import com.android.ide.common.rendering.api.IProjectCallback;
20 import com.android.ide.common.rendering.api.LayoutLog;
21 import com.android.ide.common.rendering.api.ResourceValue;
22 import com.android.layoutlib.bridge.Bridge;
23 import com.android.layoutlib.bridge.BridgeConstants;
24 import com.android.layoutlib.bridge.impl.ResourceHelper;
25 import com.android.ninepatch.NinePatch;
26 import com.android.resources.ResourceType;
27 import com.android.util.Pair;
28
29 import org.kxml2.io.KXmlParser;
30 import org.xmlpull.v1.XmlPullParser;
31 import org.xmlpull.v1.XmlPullParserException;
32
33 import android.content.res.AssetFileDescriptor;
34 import android.content.res.AssetManager;
35 import android.content.res.ColorStateList;
36 import android.content.res.Configuration;
37 import android.content.res.Resources;
38 import android.content.res.TypedArray;
39 import android.content.res.XmlResourceParser;
40 import android.graphics.drawable.Drawable;
41 import android.util.AttributeSet;
42 import android.util.DisplayMetrics;
43 import android.util.TypedValue;
44 import android.view.ViewGroup.LayoutParams;
45
46 import java.io.File;
47 import java.io.FileInputStream;
48 import java.io.FileNotFoundException;
49 import java.io.InputStream;
50
51 /**
52  *
53  */
54 public final class BridgeResources extends Resources {
55
56     private BridgeContext mContext;
57     private IProjectCallback mProjectCallback;
58     private boolean[] mPlatformResourceFlag = new boolean[1];
59
60     /**
61      * Simpler wrapper around FileInputStream. This is used when the input stream represent
62      * not a normal bitmap but a nine patch.
63      * This is useful when the InputStream is created in a method but used in another that needs
64      * to know whether this is 9-patch or not, such as BitmapFactory.
65      */
66     public class NinePatchInputStream extends FileInputStream {
67         private boolean mFakeMarkSupport = true;
68         public NinePatchInputStream(File file) throws FileNotFoundException {
69             super(file);
70         }
71
72         @Override
73         public boolean markSupported() {
74             if (mFakeMarkSupport) {
75                 // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
76                 return true;
77             }
78
79             return super.markSupported();
80         }
81
82         public void disableFakeMarkSupport() {
83             // disable fake mark support so that in case codec actually try to use them
84             // we don't lie to them.
85             mFakeMarkSupport = false;
86         }
87     }
88
89     /**
90      * This initializes the static field {@link Resources#mSystem} which is used
91      * by methods who get global resources using {@link Resources#getSystem()}.
92      * <p/>
93      * They will end up using our bridge resources.
94      * <p/>
95      * {@link Bridge} calls this method after setting up a new bridge.
96      */
97     /*package*/ static Resources initSystem(BridgeContext context,
98             AssetManager assets,
99             DisplayMetrics metrics,
100             Configuration config,
101             IProjectCallback projectCallback) {
102         return Resources.mSystem = new BridgeResources(context,
103                 assets,
104                 metrics,
105                 config,
106                 projectCallback);
107     }
108
109     /**
110      * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects
111      * around that would prevent us from unloading the library.
112      */
113     /*package*/ static void disposeSystem() {
114         if (Resources.mSystem instanceof BridgeResources) {
115             ((BridgeResources)(Resources.mSystem)).mContext = null;
116             ((BridgeResources)(Resources.mSystem)).mProjectCallback = null;
117         }
118         Resources.mSystem = null;
119     }
120
121     private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics,
122             Configuration config, IProjectCallback projectCallback) {
123         super(assets, metrics, config);
124         mContext = context;
125         mProjectCallback = projectCallback;
126     }
127
128     public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) {
129         return new BridgeTypedArray(this, mContext, numEntries, platformFile);
130     }
131
132     private ResourceValue getResourceValue(int id, boolean[] platformResFlag_out) {
133         // first get the String related to this id in the framework
134         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
135
136         if (resourceInfo != null) {
137             platformResFlag_out[0] = true;
138             return mContext.getRenderResources().getFrameworkResource(
139                     resourceInfo.getFirst(), resourceInfo.getSecond());
140         }
141
142         // didn't find a match in the framework? look in the project.
143         if (mProjectCallback != null) {
144             resourceInfo = mProjectCallback.resolveResourceId(id);
145
146             if (resourceInfo != null) {
147                 platformResFlag_out[0] = false;
148                 return mContext.getRenderResources().getProjectResource(
149                         resourceInfo.getFirst(), resourceInfo.getSecond());
150             }
151         }
152
153         return null;
154     }
155
156     @Override
157     public Drawable getDrawable(int id) throws NotFoundException {
158         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
159
160         if (value != null) {
161             return ResourceHelper.getDrawable(value, mContext);
162         }
163
164         // id was not found or not resolved. Throw a NotFoundException.
165         throwException(id);
166
167         // this is not used since the method above always throws
168         return null;
169     }
170
171     @Override
172     public int getColor(int id) throws NotFoundException {
173         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
174
175         if (value != null) {
176             try {
177                 return ResourceHelper.getColor(value.getValue());
178             } catch (NumberFormatException e) {
179                 Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e,
180                         null /*data*/);
181                 return 0;
182             }
183         }
184
185         // id was not found or not resolved. Throw a NotFoundException.
186         throwException(id);
187
188         // this is not used since the method above always throws
189         return 0;
190     }
191
192     @Override
193     public ColorStateList getColorStateList(int id) throws NotFoundException {
194         ResourceValue resValue = getResourceValue(id, mPlatformResourceFlag);
195
196         if (resValue != null) {
197             ColorStateList stateList = ResourceHelper.getColorStateList(resValue, mContext);
198             if (stateList != null) {
199                 return stateList;
200             }
201         }
202
203         // id was not found or not resolved. Throw a NotFoundException.
204         throwException(id);
205
206         // this is not used since the method above always throws
207         return null;
208     }
209
210     @Override
211     public CharSequence getText(int id) throws NotFoundException {
212         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
213
214         if (value != null) {
215             return value.getValue();
216         }
217
218         // id was not found or not resolved. Throw a NotFoundException.
219         throwException(id);
220
221         // this is not used since the method above always throws
222         return null;
223     }
224
225     @Override
226     public XmlResourceParser getLayout(int id) throws NotFoundException {
227         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
228
229         if (value != null) {
230             XmlPullParser parser = null;
231
232             try {
233                 // check if the current parser can provide us with a custom parser.
234                 if (mPlatformResourceFlag[0] == false) {
235                     parser = mProjectCallback.getParser(value.getName());
236                 }
237
238                 // create a new one manually if needed.
239                 if (parser == null) {
240                     File xml = new File(value.getValue());
241                     if (xml.isFile()) {
242                         // we need to create a pull parser around the layout XML file, and then
243                         // give that to our XmlBlockParser
244                         parser = new KXmlParser();
245                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
246                         parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
247                     }
248                 }
249
250                 if (parser != null) {
251                     return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
252                 }
253             } catch (XmlPullParserException e) {
254                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
255                         "Failed to configure parser for " + value.getValue(), e, null /*data*/);
256                 // we'll return null below.
257             } catch (FileNotFoundException e) {
258                 // this shouldn't happen since we check above.
259             }
260
261         }
262
263         // id was not found or not resolved. Throw a NotFoundException.
264         throwException(id);
265
266         // this is not used since the method above always throws
267         return null;
268     }
269
270     @Override
271     public XmlResourceParser getAnimation(int id) throws NotFoundException {
272         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
273
274         if (value != null) {
275             XmlPullParser parser = null;
276
277             try {
278                 File xml = new File(value.getValue());
279                 if (xml.isFile()) {
280                     // we need to create a pull parser around the layout XML file, and then
281                     // give that to our XmlBlockParser
282                     parser = new KXmlParser();
283                     parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
284                     parser.setInput(new FileInputStream(xml), "UTF-8"); //$NON-NLS-1$);
285
286                     return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
287                 }
288             } catch (XmlPullParserException e) {
289                 Bridge.getLog().error(LayoutLog.TAG_BROKEN,
290                         "Failed to configure parser for " + value.getValue(), e, null /*data*/);
291                 // we'll return null below.
292             } catch (FileNotFoundException e) {
293                 // this shouldn't happen since we check above.
294             }
295
296         }
297
298         // id was not found or not resolved. Throw a NotFoundException.
299         throwException(id);
300
301         // this is not used since the method above always throws
302         return null;
303     }
304
305     @Override
306     public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
307         return mContext.obtainStyledAttributes(set, attrs);
308     }
309
310     @Override
311     public TypedArray obtainTypedArray(int id) throws NotFoundException {
312         throw new UnsupportedOperationException();
313     }
314
315
316     @Override
317     public float getDimension(int id) throws NotFoundException {
318         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
319
320         if (value != null) {
321             String v = value.getValue();
322
323             if (v != null) {
324                 if (v.equals(BridgeConstants.MATCH_PARENT) ||
325                         v.equals(BridgeConstants.FILL_PARENT)) {
326                     return LayoutParams.MATCH_PARENT;
327                 } else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
328                     return LayoutParams.WRAP_CONTENT;
329                 }
330
331                 if (ResourceHelper.stringToFloat(v, mTmpValue) &&
332                         mTmpValue.type == TypedValue.TYPE_DIMENSION) {
333                     return mTmpValue.getDimension(mMetrics);
334                 }
335             }
336         }
337
338         // id was not found or not resolved. Throw a NotFoundException.
339         throwException(id);
340
341         // this is not used since the method above always throws
342         return 0;
343     }
344
345     @Override
346     public int getDimensionPixelOffset(int id) throws NotFoundException {
347         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
348
349         if (value != null) {
350             String v = value.getValue();
351
352             if (v != null) {
353                 if (ResourceHelper.stringToFloat(v, mTmpValue) &&
354                         mTmpValue.type == TypedValue.TYPE_DIMENSION) {
355                     return TypedValue.complexToDimensionPixelOffset(mTmpValue.data, mMetrics);
356                 }
357             }
358         }
359
360         // id was not found or not resolved. Throw a NotFoundException.
361         throwException(id);
362
363         // this is not used since the method above always throws
364         return 0;
365     }
366
367     @Override
368     public int getDimensionPixelSize(int id) throws NotFoundException {
369         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
370
371         if (value != null) {
372             String v = value.getValue();
373
374             if (v != null) {
375                 if (ResourceHelper.stringToFloat(v, mTmpValue) &&
376                         mTmpValue.type == TypedValue.TYPE_DIMENSION) {
377                     return TypedValue.complexToDimensionPixelSize(mTmpValue.data, mMetrics);
378                 }
379             }
380         }
381
382         // id was not found or not resolved. Throw a NotFoundException.
383         throwException(id);
384
385         // this is not used since the method above always throws
386         return 0;
387     }
388
389     @Override
390     public int getInteger(int id) throws NotFoundException {
391         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
392
393         if (value != null && value.getValue() != null) {
394             String v = value.getValue();
395             int radix = 10;
396             if (v.startsWith("0x")) {
397                 v = v.substring(2);
398                 radix = 16;
399             }
400             try {
401                 return Integer.parseInt(v, radix);
402             } catch (NumberFormatException e) {
403                 // return exception below
404             }
405         }
406
407         // id was not found or not resolved. Throw a NotFoundException.
408         throwException(id);
409
410         // this is not used since the method above always throws
411         return 0;
412     }
413
414     @Override
415     public String getResourceEntryName(int resid) throws NotFoundException {
416         throw new UnsupportedOperationException();
417     }
418
419     @Override
420     public String getResourceName(int resid) throws NotFoundException {
421         throw new UnsupportedOperationException();
422     }
423
424     @Override
425     public String getResourceTypeName(int resid) throws NotFoundException {
426         throw new UnsupportedOperationException();
427     }
428
429     @Override
430     public String getString(int id, Object... formatArgs) throws NotFoundException {
431         String s = getString(id);
432         if (s != null) {
433             return String.format(s, formatArgs);
434
435         }
436
437         // id was not found or not resolved. Throw a NotFoundException.
438         throwException(id);
439
440         // this is not used since the method above always throws
441         return null;
442     }
443
444     @Override
445     public String getString(int id) throws NotFoundException {
446         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
447
448         if (value != null && value.getValue() != null) {
449             return value.getValue();
450         }
451
452         // id was not found or not resolved. Throw a NotFoundException.
453         throwException(id);
454
455         // this is not used since the method above always throws
456         return null;
457     }
458
459     @Override
460     public void getValue(int id, TypedValue outValue, boolean resolveRefs)
461             throws NotFoundException {
462         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
463
464         if (value != null) {
465             String v = value.getValue();
466
467             if (v != null) {
468                 if (ResourceHelper.stringToFloat(v, outValue)) {
469                     return;
470                 }
471
472                 // else it's a string
473                 outValue.type = TypedValue.TYPE_STRING;
474                 outValue.string = v;
475                 return;
476             }
477         }
478
479         // id was not found or not resolved. Throw a NotFoundException.
480         throwException(id);
481     }
482
483     @Override
484     public void getValue(String name, TypedValue outValue, boolean resolveRefs)
485             throws NotFoundException {
486         throw new UnsupportedOperationException();
487     }
488
489     @Override
490     public XmlResourceParser getXml(int id) throws NotFoundException {
491         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
492
493         if (value != null) {
494             String v = value.getValue();
495
496             if (v != null) {
497                 // check this is a file
498                 File f = new File(value.getValue());
499                 if (f.isFile()) {
500                     try {
501                         KXmlParser parser = new KXmlParser();
502                         parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
503                         parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
504
505                         return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
506                     } catch (XmlPullParserException e) {
507                         NotFoundException newE = new NotFoundException();
508                         newE.initCause(e);
509                         throw newE;
510                     } catch (FileNotFoundException e) {
511                         NotFoundException newE = new NotFoundException();
512                         newE.initCause(e);
513                         throw newE;
514                     }
515                 }
516             }
517         }
518
519         // id was not found or not resolved. Throw a NotFoundException.
520         throwException(id);
521
522         // this is not used since the method above always throws
523         return null;
524     }
525
526     @Override
527     public XmlResourceParser loadXmlResourceParser(String file, int id,
528             int assetCookie, String type) throws NotFoundException {
529         // even though we know the XML file to load directly, we still need to resolve the
530         // id so that we can know if it's a platform or project resource.
531         // (mPlatformResouceFlag will get the result and will be used later).
532         getResourceValue(id, mPlatformResourceFlag);
533
534         File f = new File(file);
535         try {
536             KXmlParser parser = new KXmlParser();
537             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
538             parser.setInput(new FileInputStream(f), "UTF-8"); //$NON-NLS-1$);
539
540             return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
541         } catch (XmlPullParserException e) {
542             NotFoundException newE = new NotFoundException();
543             newE.initCause(e);
544             throw newE;
545         } catch (FileNotFoundException e) {
546             NotFoundException newE = new NotFoundException();
547             newE.initCause(e);
548             throw newE;
549         }
550     }
551
552
553     @Override
554     public InputStream openRawResource(int id) throws NotFoundException {
555         ResourceValue value = getResourceValue(id, mPlatformResourceFlag);
556
557         if (value != null) {
558             String path = value.getValue();
559
560             if (path != null) {
561                 // check this is a file
562                 File f = new File(path);
563                 if (f.isFile()) {
564                     try {
565                         // if it's a nine-patch return a custom input stream so that
566                         // other methods (mainly bitmap factory) can detect it's a 9-patch
567                         // and actually load it as a 9-patch instead of a normal bitmap
568                         if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
569                             return new NinePatchInputStream(f);
570                         }
571                         return new FileInputStream(f);
572                     } catch (FileNotFoundException e) {
573                         NotFoundException newE = new NotFoundException();
574                         newE.initCause(e);
575                         throw newE;
576                     }
577                 }
578             }
579         }
580
581         // id was not found or not resolved. Throw a NotFoundException.
582         throwException(id);
583
584         // this is not used since the method above always throws
585         return null;
586     }
587
588     @Override
589     public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
590         getValue(id, value, true);
591
592         String path = value.string.toString();
593
594         File f = new File(path);
595         if (f.isFile()) {
596             try {
597                 // if it's a nine-patch return a custom input stream so that
598                 // other methods (mainly bitmap factory) can detect it's a 9-patch
599                 // and actually load it as a 9-patch instead of a normal bitmap
600                 if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
601                     return new NinePatchInputStream(f);
602                 }
603                 return new FileInputStream(f);
604             } catch (FileNotFoundException e) {
605                 NotFoundException exception = new NotFoundException();
606                 exception.initCause(e);
607                 throw exception;
608             }
609         }
610
611         throw new NotFoundException();
612     }
613
614     @Override
615     public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
616         throw new UnsupportedOperationException();
617     }
618
619     /**
620      * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type.
621      * @param id the id of the resource
622      * @throws NotFoundException
623      */
624     private void throwException(int id) throws NotFoundException {
625         // first get the String related to this id in the framework
626         Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
627
628         // if the name is unknown in the framework, get it from the custom view loader.
629         if (resourceInfo == null && mProjectCallback != null) {
630             resourceInfo = mProjectCallback.resolveResourceId(id);
631         }
632
633         String message = null;
634         if (resourceInfo != null) {
635             message = String.format(
636                     "Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.",
637                     resourceInfo.getFirst(), id, resourceInfo.getSecond());
638         } else {
639             message = String.format(
640                     "Could not resolve resource value: 0x%1$X.", id);
641         }
642
643         throw new NotFoundException(message);
644     }
645 }