blob: 0bcc7fd680dd2b7987a9024588de4aad09ae5958 [file] [log] [blame]
/*
* 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);
}
}