| /* |
| * Copyright (C) 2008 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.layoutlib.bridge; |
| |
| import com.android.layoutlib.api.IProjectCallback; |
| import com.android.layoutlib.api.IResourceValue; |
| |
| import org.kxml2.io.KXmlParser; |
| import org.xmlpull.v1.XmlPullParser; |
| import org.xmlpull.v1.XmlPullParserException; |
| |
| import android.content.res.AssetFileDescriptor; |
| import android.content.res.AssetManager; |
| import android.content.res.ColorStateList; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.content.res.TypedArray; |
| import android.content.res.XmlResourceParser; |
| import android.graphics.drawable.Drawable; |
| import android.util.AttributeSet; |
| import android.util.DisplayMetrics; |
| import android.util.TypedValue; |
| import android.view.ViewGroup.LayoutParams; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileNotFoundException; |
| import java.io.FileReader; |
| import java.io.InputStream; |
| |
| /** |
| * |
| */ |
| public final class BridgeResources extends Resources { |
| |
| private BridgeContext mContext; |
| private IProjectCallback mProjectCallback; |
| private boolean[] mPlatformResourceFlag = new boolean[1]; |
| |
| /** |
| * This initializes the static field {@link Resources#mSystem} which is used |
| * by methods who get global resources using {@link Resources#getSystem()}. |
| * <p/> |
| * They will end up using our bridge resources. |
| * <p/> |
| * {@link Bridge} calls this method after setting up a new bridge. |
| */ |
| /*package*/ static Resources initSystem(BridgeContext context, |
| AssetManager assets, |
| DisplayMetrics metrics, |
| Configuration config, |
| IProjectCallback projectCallback) { |
| if (!(Resources.mSystem instanceof BridgeResources)) { |
| Resources.mSystem = new BridgeResources(context, |
| assets, |
| metrics, |
| config, |
| projectCallback); |
| } |
| return Resources.mSystem; |
| } |
| |
| /** |
| * Clears the static {@link Resources#mSystem} to make sure we don't leave objects |
| * around that would prevent us from unloading the library. |
| */ |
| /*package*/ static void clearSystem() { |
| if (Resources.mSystem instanceof BridgeResources) { |
| ((BridgeResources)(Resources.mSystem)).mContext = null; |
| ((BridgeResources)(Resources.mSystem)).mProjectCallback = null; |
| } |
| Resources.mSystem = null; |
| } |
| |
| private BridgeResources(BridgeContext context, AssetManager assets, DisplayMetrics metrics, |
| Configuration config, IProjectCallback projectCallback) { |
| super(assets, metrics, config); |
| mContext = context; |
| mProjectCallback = projectCallback; |
| } |
| |
| public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) { |
| return new BridgeTypedArray(this, mContext, numEntries, platformFile); |
| } |
| |
| private IResourceValue getResourceValue(int id, boolean[] platformResFlag_out) { |
| // first get the String related to this id in the framework |
| String[] resourceInfo = Bridge.resolveResourceValue(id); |
| |
| if (resourceInfo != null) { |
| platformResFlag_out[0] = true; |
| return mContext.getFrameworkResource(resourceInfo[1], resourceInfo[0]); |
| } |
| |
| // didn't find a match in the framework? look in the project. |
| if (mProjectCallback != null) { |
| resourceInfo = mProjectCallback.resolveResourceValue(id); |
| |
| if (resourceInfo != null) { |
| platformResFlag_out[0] = false; |
| return mContext.getProjectResource(resourceInfo[1], resourceInfo[0]); |
| } |
| } |
| |
| return null; |
| } |
| |
| @Override |
| public Drawable getDrawable(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| return ResourceHelper.getDrawable(value.getValue(), mContext, value.isFramework()); |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public int getColor(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| try { |
| return ResourceHelper.getColor(value.getValue()); |
| } catch (NumberFormatException e) { |
| return 0; |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return 0; |
| } |
| |
| @Override |
| public ColorStateList getColorStateList(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| try { |
| int color = ResourceHelper.getColor(value.getValue()); |
| return ColorStateList.valueOf(color); |
| } catch (NumberFormatException e) { |
| return null; |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public CharSequence getText(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| return value.getValue(); |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public XmlResourceParser getLayout(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| File xml = new File(value.getValue()); |
| if (xml.isFile()) { |
| // we need to create a pull parser around the layout XML file, and then |
| // give that to our XmlBlockParser |
| try { |
| KXmlParser parser = new KXmlParser(); |
| parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); |
| parser.setInput(new FileReader(xml)); |
| |
| return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); |
| } catch (XmlPullParserException e) { |
| mContext.getLogger().error(e); |
| |
| // we'll return null below. |
| } catch (FileNotFoundException e) { |
| // this shouldn't happen since we check above. |
| } |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public TypedArray obtainAttributes(AttributeSet set, int[] attrs) { |
| return mContext.obtainStyledAttributes(set, attrs); |
| } |
| |
| @Override |
| public TypedArray obtainTypedArray(int id) throws NotFoundException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| |
| @Override |
| public float getDimension(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| String v = value.getValue(); |
| |
| if (v != null) { |
| if (v.equals(BridgeConstants.FILL_PARENT)) { |
| return LayoutParams.FILL_PARENT; |
| } else if (v.equals(BridgeConstants.WRAP_CONTENT)) { |
| return LayoutParams.WRAP_CONTENT; |
| } |
| |
| if (ResourceHelper.stringToFloat(v, mTmpValue) && |
| mTmpValue.type == TypedValue.TYPE_DIMENSION) { |
| return mTmpValue.getDimension(mMetrics); |
| } |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return 0; |
| } |
| |
| @Override |
| public int getDimensionPixelOffset(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| String v = value.getValue(); |
| |
| if (v != null) { |
| if (ResourceHelper.stringToFloat(v, mTmpValue) && |
| mTmpValue.type == TypedValue.TYPE_DIMENSION) { |
| return TypedValue.complexToDimensionPixelOffset(mTmpValue.data, mMetrics); |
| } |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return 0; |
| } |
| |
| @Override |
| public int getDimensionPixelSize(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| String v = value.getValue(); |
| |
| if (v != null) { |
| if (ResourceHelper.stringToFloat(v, mTmpValue) && |
| mTmpValue.type == TypedValue.TYPE_DIMENSION) { |
| return TypedValue.complexToDimensionPixelSize(mTmpValue.data, mMetrics); |
| } |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return 0; |
| } |
| |
| @Override |
| public int getInteger(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null && value.getValue() != null) { |
| String v = value.getValue(); |
| int radix = 10; |
| if (v.startsWith("0x")) { |
| v = v.substring(2); |
| radix = 16; |
| } |
| try { |
| return Integer.parseInt(v, radix); |
| } catch (NumberFormatException e) { |
| // return exception below |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return 0; |
| } |
| |
| @Override |
| public String getResourceEntryName(int resid) throws NotFoundException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public String getResourceName(int resid) throws NotFoundException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public String getResourceTypeName(int resid) throws NotFoundException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public String getString(int id, Object... formatArgs) throws NotFoundException { |
| String s = getString(id); |
| if (s != null) { |
| return String.format(s, formatArgs); |
| |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public String getString(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null && value.getValue() != null) { |
| return value.getValue(); |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public void getValue(int id, TypedValue outValue, boolean resolveRefs) |
| throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| String v = value.getValue(); |
| |
| if (v != null) { |
| if (ResourceHelper.stringToFloat(v, outValue)) { |
| return; |
| } |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| } |
| |
| @Override |
| public void getValue(String name, TypedValue outValue, boolean resolveRefs) |
| throws NotFoundException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public XmlResourceParser getXml(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| String v = value.getValue(); |
| |
| if (v != null) { |
| // check this is a file |
| File f = new File(value.getValue()); |
| if (f.isFile()) { |
| try { |
| KXmlParser parser = new KXmlParser(); |
| parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); |
| parser.setInput(new FileReader(f)); |
| |
| return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); |
| } catch (XmlPullParserException e) { |
| NotFoundException newE = new NotFoundException(); |
| newE.initCause(e); |
| throw newE; |
| } catch (FileNotFoundException e) { |
| NotFoundException newE = new NotFoundException(); |
| newE.initCause(e); |
| throw newE; |
| } |
| } |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public InputStream openRawResource(int id) throws NotFoundException { |
| IResourceValue value = getResourceValue(id, mPlatformResourceFlag); |
| |
| if (value != null) { |
| String v = value.getValue(); |
| |
| if (v != null) { |
| // check this is a file |
| File f = new File(value.getValue()); |
| if (f.isFile()) { |
| try { |
| return new FileInputStream(f); |
| } catch (FileNotFoundException e) { |
| NotFoundException newE = new NotFoundException(); |
| newE.initCause(e); |
| throw newE; |
| } |
| } |
| } |
| } |
| |
| // id was not found or not resolved. Throw a NotFoundException. |
| throwException(id); |
| |
| // this is not used since the method above always throws |
| return null; |
| } |
| |
| @Override |
| public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| /** |
| * Builds and throws a {@link Resources.NotFoundException} based on a resource id and a resource type. |
| * @param id the id of the resource |
| * @throws NotFoundException |
| */ |
| private void throwException(int id) throws NotFoundException { |
| // first get the String related to this id in the framework |
| String[] resourceInfo = Bridge.resolveResourceValue(id); |
| |
| // if the name is unknown in the framework, get it from the custom view loader. |
| if (resourceInfo == null && mProjectCallback != null) { |
| resourceInfo = mProjectCallback.resolveResourceValue(id); |
| } |
| |
| String message = null; |
| if (resourceInfo != null) { |
| message = String.format( |
| "Could not find %1$s resource matching value 0x%2$X (resolved name: %3$s) in current configuration.", |
| resourceInfo[1], id, resourceInfo[0]); |
| } else { |
| message = String.format( |
| "Could not resolve resource value: 0x%1$X.", id); |
| } |
| |
| throw new NotFoundException(message); |
| } |
| } |