summaryrefslogtreecommitdiffstats
path: root/tools/layoutlib/bridge/src/android
diff options
context:
space:
mode:
Diffstat (limited to 'tools/layoutlib/bridge/src/android')
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeResources.java647
-rw-r--r--tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java909
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java2
-rw-r--r--tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java38
4 files changed, 1563 insertions, 33 deletions
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
new file mode 100644
index 0000000..e039898
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeResources.java
@@ -0,0 +1,647 @@
+/*
+ * 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 android.content.res;
+
+import com.android.ide.common.rendering.api.IProjectCallback;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.impl.ParserFactory;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.ninepatch.NinePatch;
+import com.android.resources.ResourceType;
+import com.android.util.Pair;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+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.InputStream;
+
+/**
+ *
+ */
+public final class BridgeResources extends Resources {
+
+ private BridgeContext mContext;
+ private IProjectCallback mProjectCallback;
+ private boolean[] mPlatformResourceFlag = new boolean[1];
+
+ /**
+ * Simpler wrapper around FileInputStream. This is used when the input stream represent
+ * not a normal bitmap but a nine patch.
+ * This is useful when the InputStream is created in a method but used in another that needs
+ * to know whether this is 9-patch or not, such as BitmapFactory.
+ */
+ public class NinePatchInputStream extends FileInputStream {
+ private boolean mFakeMarkSupport = true;
+ public NinePatchInputStream(File file) throws FileNotFoundException {
+ super(file);
+ }
+
+ @Override
+ public boolean markSupported() {
+ if (mFakeMarkSupport) {
+ // this is needed so that BitmapFactory doesn't wrap this in a BufferedInputStream.
+ return true;
+ }
+
+ return super.markSupported();
+ }
+
+ public void disableFakeMarkSupport() {
+ // disable fake mark support so that in case codec actually try to use them
+ // we don't lie to them.
+ mFakeMarkSupport = false;
+ }
+ }
+
+ /**
+ * 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*/ public static Resources initSystem(BridgeContext context,
+ AssetManager assets,
+ DisplayMetrics metrics,
+ Configuration config,
+ IProjectCallback projectCallback) {
+ return Resources.mSystem = new BridgeResources(context,
+ assets,
+ metrics,
+ config,
+ projectCallback);
+ }
+
+ /**
+ * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects
+ * around that would prevent us from unloading the library.
+ */
+ /*package*/ public static void disposeSystem() {
+ 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,
+ boolean platformStyleable, String styleableName) {
+ return new BridgeTypedArray(this, mContext, numEntries, platformFile,
+ platformStyleable, styleableName);
+ }
+
+ private Pair<String, ResourceValue> getResourceValue(int id, boolean[] platformResFlag_out) {
+ // first get the String related to this id in the framework
+ Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+
+ if (resourceInfo != null) {
+ platformResFlag_out[0] = true;
+ String attributeName = resourceInfo.getSecond();
+
+ return Pair.of(attributeName, mContext.getRenderResources().getFrameworkResource(
+ resourceInfo.getFirst(), attributeName));
+ }
+
+ // didn't find a match in the framework? look in the project.
+ if (mProjectCallback != null) {
+ resourceInfo = mProjectCallback.resolveResourceId(id);
+
+ if (resourceInfo != null) {
+ platformResFlag_out[0] = false;
+ String attributeName = resourceInfo.getSecond();
+
+ return Pair.of(attributeName, mContext.getRenderResources().getProjectResource(
+ resourceInfo.getFirst(), attributeName));
+ }
+ }
+
+ return null;
+ }
+
+ @Override
+ public Drawable getDrawable(int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ return ResourceHelper.getDrawable(value.getSecond(), mContext);
+ }
+
+ // 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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ try {
+ return ResourceHelper.getColor(value.getSecond().getValue());
+ } catch (NumberFormatException e) {
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e,
+ null /*data*/);
+ 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 {
+ Pair<String, ResourceValue> resValue = getResourceValue(id, mPlatformResourceFlag);
+
+ if (resValue != null) {
+ ColorStateList stateList = ResourceHelper.getColorStateList(resValue.getSecond(),
+ mContext);
+ if (stateList != null) {
+ return stateList;
+ }
+ }
+
+ // 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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ return value.getSecond().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 {
+ Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+
+ if (v != null) {
+ ResourceValue value = v.getSecond();
+ XmlPullParser parser = null;
+
+ try {
+ // check if the current parser can provide us with a custom parser.
+ if (mPlatformResourceFlag[0] == false) {
+ parser = mProjectCallback.getParser(value);
+ }
+
+ // create a new one manually if needed.
+ if (parser == 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
+ parser = ParserFactory.create(xml);
+ }
+ }
+
+ if (parser != null) {
+ return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+ }
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Failed to configure parser for " + value.getValue(), e, null /*data*/);
+ // 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 XmlResourceParser getAnimation(int id) throws NotFoundException {
+ Pair<String, ResourceValue> v = getResourceValue(id, mPlatformResourceFlag);
+
+ if (v != null) {
+ ResourceValue value = v.getSecond();
+ XmlPullParser parser = null;
+
+ try {
+ 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
+ parser = ParserFactory.create(xml);
+
+ return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]);
+ }
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Failed to configure parser for " + value.getValue(), e, null /*data*/);
+ // 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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ String v = value.getSecond().getValue();
+
+ if (v != null) {
+ if (v.equals(BridgeConstants.MATCH_PARENT) ||
+ v.equals(BridgeConstants.FILL_PARENT)) {
+ return LayoutParams.MATCH_PARENT;
+ } else if (v.equals(BridgeConstants.WRAP_CONTENT)) {
+ return LayoutParams.WRAP_CONTENT;
+ }
+
+ if (ResourceHelper.parseFloatAttribute(
+ value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
+ mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+ return mTmpValue.getDimension(getDisplayMetrics());
+ }
+ }
+ }
+
+ // 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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ String v = value.getSecond().getValue();
+
+ if (v != null) {
+ if (ResourceHelper.parseFloatAttribute(
+ value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
+ mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelOffset(mTmpValue.data,
+ getDisplayMetrics());
+ }
+ }
+ }
+
+ // 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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ String v = value.getSecond().getValue();
+
+ if (v != null) {
+ if (ResourceHelper.parseFloatAttribute(
+ value.getFirst(), v, mTmpValue, true /*requireUnit*/) &&
+ mTmpValue.type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelSize(mTmpValue.data,
+ getDisplayMetrics());
+ }
+ }
+ }
+
+ // 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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null && value.getSecond().getValue() != null) {
+ String v = value.getSecond().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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null && value.getSecond().getValue() != null) {
+ return value.getSecond().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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ String v = value.getSecond().getValue();
+
+ if (v != null) {
+ if (ResourceHelper.parseFloatAttribute(value.getFirst(), v, outValue,
+ false /*requireUnit*/)) {
+ return;
+ }
+
+ // else it's a string
+ outValue.type = TypedValue.TYPE_STRING;
+ outValue.string = v;
+ 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 {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ String v = value.getSecond().getValue();
+
+ if (v != null) {
+ // check this is a file
+ File f = new File(v);
+ if (f.isFile()) {
+ try {
+ XmlPullParser parser = ParserFactory.create(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 XmlResourceParser loadXmlResourceParser(String file, int id,
+ int assetCookie, String type) throws NotFoundException {
+ // even though we know the XML file to load directly, we still need to resolve the
+ // id so that we can know if it's a platform or project resource.
+ // (mPlatformResouceFlag will get the result and will be used later).
+ getResourceValue(id, mPlatformResourceFlag);
+
+ File f = new File(file);
+ try {
+ XmlPullParser parser = ParserFactory.create(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;
+ }
+ }
+
+
+ @Override
+ public InputStream openRawResource(int id) throws NotFoundException {
+ Pair<String, ResourceValue> value = getResourceValue(id, mPlatformResourceFlag);
+
+ if (value != null) {
+ String path = value.getSecond().getValue();
+
+ if (path != null) {
+ // check this is a file
+ File f = new File(path);
+ if (f.isFile()) {
+ try {
+ // if it's a nine-patch return a custom input stream so that
+ // other methods (mainly bitmap factory) can detect it's a 9-patch
+ // and actually load it as a 9-patch instead of a normal bitmap
+ if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
+ return new NinePatchInputStream(f);
+ }
+ 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 InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
+ getValue(id, value, true);
+
+ String path = value.string.toString();
+
+ File f = new File(path);
+ if (f.isFile()) {
+ try {
+ // if it's a nine-patch return a custom input stream so that
+ // other methods (mainly bitmap factory) can detect it's a 9-patch
+ // and actually load it as a 9-patch instead of a normal bitmap
+ if (path.toLowerCase().endsWith(NinePatch.EXTENSION_9PATCH)) {
+ return new NinePatchInputStream(f);
+ }
+ return new FileInputStream(f);
+ } catch (FileNotFoundException e) {
+ NotFoundException exception = new NotFoundException();
+ exception.initCause(e);
+ throw exception;
+ }
+ }
+
+ throw new NotFoundException();
+ }
+
+ @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
+ Pair<ResourceType, String> resourceInfo = Bridge.resolveResourceId(id);
+
+ // if the name is unknown in the framework, get it from the custom view loader.
+ if (resourceInfo == null && mProjectCallback != null) {
+ resourceInfo = mProjectCallback.resolveResourceId(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.getFirst(), id, resourceInfo.getSecond());
+ } else {
+ message = String.format(
+ "Could not resolve resource value: 0x%1$X.", id);
+ }
+
+ throw new NotFoundException(message);
+ }
+}
diff --git a/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
new file mode 100644
index 0000000..9deed32
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/content/res/BridgeTypedArray.java
@@ -0,0 +1,909 @@
+/*
+ * 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 android.content.res;
+
+import com.android.ide.common.rendering.api.DeclareStyleableResourceValue;
+import com.android.ide.common.rendering.api.LayoutLog;
+import com.android.ide.common.rendering.api.RenderResources;
+import com.android.ide.common.rendering.api.ResourceValue;
+import com.android.ide.common.rendering.api.StyleResourceValue;
+import com.android.internal.util.XmlUtils;
+import com.android.layoutlib.bridge.Bridge;
+import com.android.layoutlib.bridge.BridgeConstants;
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
+import com.android.layoutlib.bridge.impl.ParserFactory;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
+import com.android.resources.ResourceType;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.res.ColorStateList;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+import android.view.LayoutInflater_Delegate;
+import android.view.ViewGroup.LayoutParams;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.Map;
+
+/**
+ * Custom implementation of TypedArray to handle non compiled resources.
+ */
+public final class BridgeTypedArray extends TypedArray {
+
+ private final BridgeResources mBridgeResources;
+ private final BridgeContext mContext;
+ private final boolean mPlatformFile;
+ private final boolean mPlatformStyleable;
+ private final String mStyleableName;
+
+ private ResourceValue[] mResourceData;
+ private String[] mNames;
+
+ public BridgeTypedArray(BridgeResources resources, BridgeContext context, int len,
+ boolean platformFile, boolean platformStyleable, String styleableName) {
+ super(null, null, null, 0);
+ mBridgeResources = resources;
+ mContext = context;
+ mPlatformFile = platformFile;
+ mPlatformStyleable = platformStyleable;
+ mStyleableName = styleableName;
+ mResourceData = new ResourceValue[len];
+ mNames = new String[len];
+ }
+
+ /** A bridge-specific method that sets a value in the type array */
+ public void bridgeSetValue(int index, String name, ResourceValue value) {
+ mResourceData[index] = value;
+ mNames[index] = name;
+ }
+
+ /**
+ * Seals the array after all calls to {@link #bridgeSetValue(int, String, ResourceValue)} have
+ * been done.
+ * <p/>This allows to compute the list of non default values, permitting
+ * {@link #getIndexCount()} to return the proper value.
+ */
+ public void sealArray() {
+ // fills TypedArray.mIndices which is used to implement getIndexCount/getIndexAt
+ // first count the array size
+ int count = 0;
+ for (ResourceValue data : mResourceData) {
+ if (data != null) {
+ count++;
+ }
+ }
+
+ // allocate the table with an extra to store the size
+ mIndices = new int[count+1];
+ mIndices[0] = count;
+
+ // fill the array with the indices.
+ int index = 1;
+ for (int i = 0 ; i < mResourceData.length ; i++) {
+ if (mResourceData[i] != null) {
+ mIndices[index++] = i;
+ }
+ }
+ }
+
+ /**
+ * Return the number of values in this array.
+ */
+ @Override
+ public int length() {
+ return mResourceData.length;
+ }
+
+ /**
+ * Return the Resources object this array was loaded from.
+ */
+ @Override
+ public Resources getResources() {
+ return mBridgeResources;
+ }
+
+ /**
+ * Retrieve the styled string value for the attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return CharSequence holding string data. May be styled. Returns
+ * null if the attribute is not defined.
+ */
+ @Override
+ public CharSequence getText(int index) {
+ if (index < 0 || index >= mResourceData.length) {
+ return null;
+ }
+
+ if (mResourceData[index] != null) {
+ // FIXME: handle styled strings!
+ return mResourceData[index].getValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve the string value for the attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return String holding string data. Any styling information is
+ * removed. Returns null if the attribute is not defined.
+ */
+ @Override
+ public String getString(int index) {
+ if (index < 0 || index >= mResourceData.length) {
+ return null;
+ }
+
+ if (mResourceData[index] != null) {
+ return mResourceData[index].getValue();
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve the boolean value for the attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined.
+ *
+ * @return Attribute boolean value, or defValue if not defined.
+ */
+ @Override
+ public boolean getBoolean(int index, boolean defValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return defValue;
+ }
+
+ if (mResourceData[index] == null) {
+ return defValue;
+ }
+
+ String s = mResourceData[index].getValue();
+ if (s != null) {
+ return XmlUtils.convertValueToBoolean(s, defValue);
+ }
+
+ return defValue;
+ }
+
+ /**
+ * Retrieve the integer value for the attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined.
+ *
+ * @return Attribute int value, or defValue if not defined.
+ */
+ @Override
+ public int getInt(int index, int defValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return defValue;
+ }
+
+ if (mResourceData[index] == null) {
+ return defValue;
+ }
+
+ String s = mResourceData[index].getValue();
+
+ if (RenderResources.REFERENCE_NULL.equals(s)) {
+ return defValue;
+ }
+
+ try {
+ return (s == null) ? defValue : XmlUtils.convertValueToInt(s, defValue);
+ } catch (NumberFormatException e) {
+ // pass
+ }
+
+ // Field is not null and is not an integer.
+ // Check for possible constants and try to find them.
+ // Get the map of attribute-constant -> IntegerValue
+ Map<String, Integer> map = null;
+ if (mPlatformStyleable) {
+ map = Bridge.getEnumValues(mNames[index]);
+ } else if (mStyleableName != null) {
+ // get the styleable matching the resolved name
+ RenderResources res = mContext.getRenderResources();
+ ResourceValue styleable = res.getProjectResource(ResourceType.DECLARE_STYLEABLE,
+ mStyleableName);
+ if (styleable instanceof DeclareStyleableResourceValue) {
+ map = ((DeclareStyleableResourceValue) styleable).getAttributeValues(mNames[index]);
+ }
+ }
+
+ if (map != null) {
+ // accumulator to store the value of the 1+ constants.
+ int result = 0;
+
+ // split the value in case this is a mix of several flags.
+ String[] keywords = s.split("\\|");
+ for (String keyword : keywords) {
+ Integer i = map.get(keyword.trim());
+ if (i != null) {
+ result |= i.intValue();
+ } else {
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%s\" in attribute \"%2$s\" is not a valid value",
+ keyword, mNames[index]), null /*data*/);
+ }
+ }
+ return result;
+ }
+
+ return defValue;
+ }
+
+ /**
+ * Retrieve the float value for the attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return Attribute float value, or defValue if not defined..
+ */
+ @Override
+ public float getFloat(int index, float defValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return defValue;
+ }
+
+ if (mResourceData[index] == null) {
+ return defValue;
+ }
+
+ String s = mResourceData[index].getValue();
+
+ if (s != null) {
+ try {
+ return Float.parseFloat(s);
+ } catch (NumberFormatException e) {
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%s\" in attribute \"%2$s\" cannot be converted to float.",
+ s, mNames[index]), null /*data*/);
+
+ // we'll return the default value below.
+ }
+ }
+ return defValue;
+ }
+
+ /**
+ * Retrieve the color value for the attribute at <var>index</var>. If
+ * the attribute references a color resource holding a complex
+ * {@link android.content.res.ColorStateList}, then the default color from
+ * the set is returned.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined or
+ * not a resource.
+ *
+ * @return Attribute color value, or defValue if not defined.
+ */
+ @Override
+ public int getColor(int index, int defValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return defValue;
+ }
+
+ if (mResourceData[index] == null) {
+ return defValue;
+ }
+
+ ColorStateList colorStateList = ResourceHelper.getColorStateList(
+ mResourceData[index], mContext);
+ if (colorStateList != null) {
+ return colorStateList.getDefaultColor();
+ }
+
+ return defValue;
+ }
+
+ /**
+ * Retrieve the ColorStateList for the attribute at <var>index</var>.
+ * The value may be either a single solid color or a reference to
+ * a color or complex {@link android.content.res.ColorStateList} description.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return ColorStateList for the attribute, or null if not defined.
+ */
+ @Override
+ public ColorStateList getColorStateList(int index) {
+ if (index < 0 || index >= mResourceData.length) {
+ return null;
+ }
+
+ if (mResourceData[index] == null) {
+ return null;
+ }
+
+ ResourceValue resValue = mResourceData[index];
+ String value = resValue.getValue();
+
+ if (value == null) {
+ return null;
+ }
+
+ if (RenderResources.REFERENCE_NULL.equals(value)) {
+ return null;
+ }
+
+ // let the framework inflate the ColorStateList from the XML file.
+ File f = new File(value);
+ if (f.isFile()) {
+ try {
+ XmlPullParser parser = ParserFactory.create(f);
+
+ BridgeXmlBlockParser blockParser = new BridgeXmlBlockParser(
+ parser, mContext, resValue.isFramework());
+ try {
+ return ColorStateList.createFromXml(mContext.getResources(), blockParser);
+ } finally {
+ blockParser.ensurePopped();
+ }
+ } catch (XmlPullParserException e) {
+ Bridge.getLog().error(LayoutLog.TAG_BROKEN,
+ "Failed to configure parser for " + value, e, null /*data*/);
+ return null;
+ } catch (Exception e) {
+ // this is an error and not warning since the file existence is checked before
+ // attempting to parse it.
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_READ,
+ "Failed to parse file " + value, e, null /*data*/);
+
+ return null;
+ }
+ }
+
+ try {
+ int color = ResourceHelper.getColor(value);
+ return ColorStateList.valueOf(color);
+ } catch (NumberFormatException e) {
+ Bridge.getLog().error(LayoutLog.TAG_RESOURCES_FORMAT, e.getMessage(), e, null /*data*/);
+ }
+
+ return null;
+ }
+
+ /**
+ * Retrieve the integer value for the attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined or
+ * not a resource.
+ *
+ * @return Attribute integer value, or defValue if not defined.
+ */
+ @Override
+ public int getInteger(int index, int defValue) {
+ return getInt(index, defValue);
+ }
+
+ /**
+ * Retrieve a dimensional unit attribute at <var>index</var>. Unit
+ * conversions are based on the current {@link DisplayMetrics}
+ * associated with the resources this {@link TypedArray} object
+ * came from.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined or
+ * not a resource.
+ *
+ * @return Attribute dimension value multiplied by the appropriate
+ * metric, or defValue if not defined.
+ *
+ * @see #getDimensionPixelOffset
+ * @see #getDimensionPixelSize
+ */
+ @Override
+ public float getDimension(int index, float defValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return defValue;
+ }
+
+ if (mResourceData[index] == null) {
+ return defValue;
+ }
+
+ String s = mResourceData[index].getValue();
+
+ if (s == null) {
+ return defValue;
+ } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+ s.equals(BridgeConstants.FILL_PARENT)) {
+ return LayoutParams.MATCH_PARENT;
+ } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+ return LayoutParams.WRAP_CONTENT;
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+ return defValue;
+ }
+
+ if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true /*requireUnit*/)) {
+ return mValue.getDimension(mBridgeResources.getDisplayMetrics());
+ }
+
+ // looks like we were unable to resolve the dimension value
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+ s, mNames[index]), null /*data*/);
+
+ return defValue;
+ }
+
+ /**
+ * Retrieve a dimensional unit attribute at <var>index</var> for use
+ * as an offset in raw pixels. This is the same as
+ * {@link #getDimension}, except the returned value is converted to
+ * integer pixels for you. An offset conversion involves simply
+ * truncating the base value to an integer.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined or
+ * not a resource.
+ *
+ * @return Attribute dimension value multiplied by the appropriate
+ * metric and truncated to integer pixels, or defValue if not defined.
+ *
+ * @see #getDimension
+ * @see #getDimensionPixelSize
+ */
+ @Override
+ public int getDimensionPixelOffset(int index, int defValue) {
+ return (int) getDimension(index, defValue);
+ }
+
+ /**
+ * Retrieve a dimensional unit attribute at <var>index</var> for use
+ * as a size in raw pixels. This is the same as
+ * {@link #getDimension}, except the returned value is converted to
+ * integer pixels for use as a size. A size conversion involves
+ * rounding the base value, and ensuring that a non-zero base value
+ * is at least one pixel in size.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined or
+ * not a resource.
+ *
+ * @return Attribute dimension value multiplied by the appropriate
+ * metric and truncated to integer pixels, or defValue if not defined.
+ *
+ * @see #getDimension
+ * @see #getDimensionPixelOffset
+ */
+ @Override
+ public int getDimensionPixelSize(int index, int defValue) {
+ try {
+ return getDimension(index);
+ } catch (RuntimeException e) {
+ if (mResourceData[index] != null) {
+ String s = mResourceData[index].getValue();
+
+ if (s != null) {
+ // looks like we were unable to resolve the dimension value
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" is not a valid format.",
+ s, mNames[index]), null /*data*/);
+ }
+ }
+
+ return defValue;
+ }
+ }
+
+ /**
+ * Special version of {@link #getDimensionPixelSize} for retrieving
+ * {@link android.view.ViewGroup}'s layout_width and layout_height
+ * attributes. This is only here for performance reasons; applications
+ * should use {@link #getDimensionPixelSize}.
+ *
+ * @param index Index of the attribute to retrieve.
+ * @param name Textual name of attribute for error reporting.
+ *
+ * @return Attribute dimension value multiplied by the appropriate
+ * metric and truncated to integer pixels.
+ */
+ @Override
+ public int getLayoutDimension(int index, String name) {
+ try {
+ // this will throw an exception
+ return getDimension(index);
+ } catch (RuntimeException e) {
+
+ if (LayoutInflater_Delegate.sIsInInclude) {
+ throw new RuntimeException();
+ }
+
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ "You must supply a " + name + " attribute.", null);
+
+ return 0;
+ }
+ }
+
+ @Override
+ public int getLayoutDimension(int index, int defValue) {
+ return getDimensionPixelSize(index, defValue);
+ }
+
+ private int getDimension(int index) {
+ if (mResourceData[index] == null) {
+ throw new RuntimeException();
+ }
+
+ String s = mResourceData[index].getValue();
+
+ if (s == null) {
+ throw new RuntimeException();
+ } else if (s.equals(BridgeConstants.MATCH_PARENT) ||
+ s.equals(BridgeConstants.FILL_PARENT)) {
+ return LayoutParams.MATCH_PARENT;
+ } else if (s.equals(BridgeConstants.WRAP_CONTENT)) {
+ return LayoutParams.WRAP_CONTENT;
+ } else if (RenderResources.REFERENCE_NULL.equals(s)) {
+ throw new RuntimeException();
+ }
+
+ if (ResourceHelper.parseFloatAttribute(mNames[index], s, mValue, true /*requireUnit*/)) {
+ float f = mValue.getDimension(mBridgeResources.getDisplayMetrics());
+
+ final int res = (int)(f+0.5f);
+ if (res != 0) return res;
+ if (f == 0) return 0;
+ if (f > 0) return 1;
+ }
+
+ throw new RuntimeException();
+ }
+
+ /**
+ * Retrieve a fractional unit attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param base The base value of this fraction. In other words, a
+ * standard fraction is multiplied by this value.
+ * @param pbase The parent base value of this fraction. In other
+ * words, a parent fraction (nn%p) is multiplied by this
+ * value.
+ * @param defValue Value to return if the attribute is not defined or
+ * not a resource.
+ *
+ * @return Attribute fractional value multiplied by the appropriate
+ * base value, or defValue if not defined.
+ */
+ @Override
+ public float getFraction(int index, int base, int pbase, float defValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return defValue;
+ }
+
+ if (mResourceData[index] == null) {
+ return defValue;
+ }
+
+ String value = mResourceData[index].getValue();
+ if (value == null) {
+ return defValue;
+ }
+
+ if (ResourceHelper.parseFloatAttribute(mNames[index], value, mValue,
+ false /*requireUnit*/)) {
+ return mValue.getFraction(base, pbase);
+ }
+
+ // looks like we were unable to resolve the fraction value
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ "\"%1$s\" in attribute \"%2$s\" cannot be converted to a fraction.",
+ value, mNames[index]), null /*data*/);
+
+ return defValue;
+ }
+
+ /**
+ * Retrieve the resource identifier for the attribute at
+ * <var>index</var>. Note that attribute resource as resolved when
+ * the overall {@link TypedArray} object is retrieved. As a
+ * result, this function will return the resource identifier of the
+ * final resource value that was found, <em>not</em> necessarily the
+ * original resource that was specified by the attribute.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param defValue Value to return if the attribute is not defined or
+ * not a resource.
+ *
+ * @return Attribute resource identifier, or defValue if not defined.
+ */
+ @Override
+ public int getResourceId(int index, int defValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return defValue;
+ }
+
+ // get the Resource for this index
+ ResourceValue resValue = mResourceData[index];
+
+ // no data, return the default value.
+ if (resValue == null) {
+ return defValue;
+ }
+
+ // check if this is a style resource
+ if (resValue instanceof StyleResourceValue) {
+ // get the id that will represent this style.
+ return mContext.getDynamicIdByStyle((StyleResourceValue)resValue);
+ }
+
+ if (RenderResources.REFERENCE_NULL.equals(resValue.getValue())) {
+ return defValue;
+ }
+
+ // if the attribute was a reference to a resource, and not a declaration of an id (@+id),
+ // then the xml attribute value was "resolved" which leads us to a ResourceValue with a
+ // valid getType() and getName() returning a resource name.
+ // (and getValue() returning null!). We need to handle this!
+ if (resValue.getResourceType() != null) {
+ // if this is a framework id
+ if (mPlatformFile || resValue.isFramework()) {
+ // look for idName in the android R classes
+ return mContext.getFrameworkResourceValue(
+ resValue.getResourceType(), resValue.getName(), defValue);
+ }
+
+ // look for idName in the project R class.
+ return mContext.getProjectResourceValue(
+ resValue.getResourceType(), resValue.getName(), defValue);
+ }
+
+ // else, try to get the value, and resolve it somehow.
+ String value = resValue.getValue();
+ if (value == null) {
+ return defValue;
+ }
+
+ // if the value is just an integer, return it.
+ try {
+ int i = Integer.parseInt(value);
+ if (Integer.toString(i).equals(value)) {
+ return i;
+ }
+ } catch (NumberFormatException e) {
+ // pass
+ }
+
+ // Handle the @id/<name>, @+id/<name> and @android:id/<name>
+ // We need to return the exact value that was compiled (from the various R classes),
+ // as these values can be reused internally with calls to findViewById().
+ // There's a trick with platform layouts that not use "android:" but their IDs are in
+ // fact in the android.R and com.android.internal.R classes.
+ // The field mPlatformFile will indicate that all IDs are to be looked up in the android R
+ // classes exclusively.
+
+ // if this is a reference to an id, find it.
+ if (value.startsWith("@id/") || value.startsWith("@+") ||
+ value.startsWith("@android:id/")) {
+
+ int pos = value.indexOf('/');
+ String idName = value.substring(pos + 1);
+
+ // if this is a framework id
+ if (mPlatformFile || value.startsWith("@android") || value.startsWith("@+android")) {
+ // look for idName in the android R classes
+ return mContext.getFrameworkResourceValue(ResourceType.ID, idName, defValue);
+ }
+
+ // look for idName in the project R class.
+ return mContext.getProjectResourceValue(ResourceType.ID, idName, defValue);
+ }
+
+ // not a direct id valid reference? resolve it
+ Integer idValue = null;
+
+ if (resValue.isFramework()) {
+ idValue = Bridge.getResourceId(resValue.getResourceType(),
+ resValue.getName());
+ } else {
+ idValue = mContext.getProjectCallback().getResourceId(
+ resValue.getResourceType(), resValue.getName());
+ }
+
+ if (idValue != null) {
+ return idValue.intValue();
+ }
+
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_RESOLVE,
+ String.format(
+ "Unable to resolve id \"%1$s\" for attribute \"%2$s\"", value, mNames[index]),
+ resValue);
+
+ return defValue;
+ }
+
+ /**
+ * Retrieve the Drawable for the attribute at <var>index</var>. This
+ * gets the resource ID of the selected attribute, and uses
+ * {@link Resources#getDrawable Resources.getDrawable} of the owning
+ * Resources object to retrieve its Drawable.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return Drawable for the attribute, or null if not defined.
+ */
+ @Override
+ public Drawable getDrawable(int index) {
+ if (index < 0 || index >= mResourceData.length) {
+ return null;
+ }
+
+ if (mResourceData[index] == null) {
+ return null;
+ }
+
+ ResourceValue value = mResourceData[index];
+ String stringValue = value.getValue();
+ if (stringValue == null || RenderResources.REFERENCE_NULL.equals(stringValue)) {
+ return null;
+ }
+
+ return ResourceHelper.getDrawable(value, mContext);
+ }
+
+
+ /**
+ * Retrieve the CharSequence[] for the attribute at <var>index</var>.
+ * This gets the resource ID of the selected attribute, and uses
+ * {@link Resources#getTextArray Resources.getTextArray} of the owning
+ * Resources object to retrieve its String[].
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return CharSequence[] for the attribute, or null if not defined.
+ */
+ @Override
+ public CharSequence[] getTextArray(int index) {
+ if (index < 0 || index >= mResourceData.length) {
+ return null;
+ }
+
+ if (mResourceData[index] == null) {
+ return null;
+ }
+
+ String value = mResourceData[index].getValue();
+ if (value != null) {
+ if (RenderResources.REFERENCE_NULL.equals(value)) {
+ return null;
+ }
+
+ return new CharSequence[] { value };
+ }
+
+ Bridge.getLog().warning(LayoutLog.TAG_RESOURCES_FORMAT,
+ String.format(
+ String.format("Unknown value for getTextArray(%d) => %s", //DEBUG
+ index, mResourceData[index].getName())), null /*data*/);
+
+ return null;
+ }
+
+ /**
+ * Retrieve the raw TypedValue for the attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ * @param outValue TypedValue object in which to place the attribute's
+ * data.
+ *
+ * @return Returns true if the value was retrieved, else false.
+ */
+ @Override
+ public boolean getValue(int index, TypedValue outValue) {
+ if (index < 0 || index >= mResourceData.length) {
+ return false;
+ }
+
+ if (mResourceData[index] == null) {
+ return false;
+ }
+
+ String s = mResourceData[index].getValue();
+
+ return ResourceHelper.parseFloatAttribute(mNames[index], s, outValue,
+ false /*requireUnit*/);
+ }
+
+ /**
+ * Determines whether there is an attribute at <var>index</var>.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return True if the attribute has a value, false otherwise.
+ */
+ @Override
+ public boolean hasValue(int index) {
+ if (index < 0 || index >= mResourceData.length) {
+ return false;
+ }
+
+ return mResourceData[index] != null;
+ }
+
+ /**
+ * Retrieve the raw TypedValue for the attribute at <var>index</var>
+ * and return a temporary object holding its data. This object is only
+ * valid until the next call on to {@link TypedArray}.
+ *
+ * @param index Index of attribute to retrieve.
+ *
+ * @return Returns a TypedValue object if the attribute is defined,
+ * containing its data; otherwise returns null. (You will not
+ * receive a TypedValue whose type is TYPE_NULL.)
+ */
+ @Override
+ public TypedValue peekValue(int index) {
+ if (index < 0 || index >= mResourceData.length) {
+ return null;
+ }
+
+ if (getValue(index, mValue)) {
+ return mValue;
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a message about the parser state suitable for printing error messages.
+ */
+ @Override
+ public String getPositionDescription() {
+ return "<internal -- stub if needed>";
+ }
+
+ /**
+ * Give back a previously retrieved StyledAttributes, for later re-use.
+ */
+ @Override
+ public void recycle() {
+ // pass
+ }
+
+ @Override
+ public boolean getValueAt(int index, TypedValue outValue) {
+ // pass
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return Arrays.toString(mResourceData);
+ }
+ }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
index 080b85f..945b3cd 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory_Delegate.java
@@ -17,12 +17,12 @@
package android.graphics;
import com.android.layoutlib.bridge.Bridge;
-import com.android.layoutlib.bridge.android.BridgeResources.NinePatchInputStream;
import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.ninepatch.NinePatchChunk;
import com.android.resources.Density;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+import android.content.res.BridgeResources.NinePatchInputStream;
import android.graphics.BitmapFactory.Options;
import java.io.FileDescriptor;
diff --git a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
index 3ef3288..64efa7e 100644
--- a/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/tools/layoutlib/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -38,11 +38,15 @@ import java.io.IOException;
*/
public class LayoutInflater_Delegate {
+ private static final String TAG_MERGE = "merge";
+
public static boolean sIsInInclude = false;
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
+ *
+ * This implementation just records the merge status before calling the default implementation.
*/
@LayoutlibDelegate
/*package*/ static void rInflate(LayoutInflater thisInflater,
@@ -58,37 +62,7 @@ public class LayoutInflater_Delegate {
// ---- START DEFAULT IMPLEMENTATION.
- final int depth = parser.getDepth();
- int type;
-
- while (((type = parser.next()) != XmlPullParser.END_TAG ||
- parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
-
- if (type != XmlPullParser.START_TAG) {
- continue;
- }
-
- final String name = parser.getName();
-
- if (LayoutInflater.TAG_REQUEST_FOCUS.equals(name)) {
- thisInflater.parseRequestFocus(parser, parent);
- } else if (LayoutInflater.TAG_INCLUDE.equals(name)) {
- if (parser.getDepth() == 0) {
- throw new InflateException("<include /> cannot be the root element");
- }
- thisInflater.parseInclude(parser, parent, attrs);
- } else if (LayoutInflater.TAG_MERGE.equals(name)) {
- throw new InflateException("<merge /> must be the root element");
- } else {
- final View view = thisInflater.createViewFromTag(parent, name, attrs);
- final ViewGroup viewGroup = (ViewGroup) parent;
- final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
- thisInflater.rInflate(parser, view, attrs, true);
- viewGroup.addView(view, params);
- }
- }
-
- if (finishInflate) parent.onFinishInflate();
+ thisInflater.rInflate_Original(parser, parent, attrs, finishInflate);
// ---- END DEFAULT IMPLEMENTATION.
@@ -138,7 +112,7 @@ public class LayoutInflater_Delegate {
final String childName = childParser.getName();
- if (LayoutInflater.TAG_MERGE.equals(childName)) {
+ if (TAG_MERGE.equals(childName)) {
// Inflate all children.
thisInflater.rInflate(childParser, parent, childAttrs, false);
} else {