auto import from //depot/cupcake/@135843
diff --git a/core/java/android/content/res/AssetFileDescriptor.java b/core/java/android/content/res/AssetFileDescriptor.java
new file mode 100644
index 0000000..231e3e2
--- /dev/null
+++ b/core/java/android/content/res/AssetFileDescriptor.java
@@ -0,0 +1,348 @@
+/*
+ * Copyright (C) 2006 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 android.os.Parcel;
+import android.os.ParcelFileDescriptor;
+import android.os.Parcelable;
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+/**
+ * File descriptor of an entry in the AssetManager. This provides your own
+ * opened FileDescriptor that can be used to read the data, as well as the
+ * offset and length of that entry's data in the file.
+ */
+public class AssetFileDescriptor implements Parcelable {
+ /**
+ * Length used with {@link #AssetFileDescriptor(ParcelFileDescriptor, long, long)}
+ * and {@link #getDeclaredLength} when a length has not been declared. This means
+ * the data extends to the end of the file.
+ */
+ public static final long UNKNOWN_LENGTH = -1;
+
+ private final ParcelFileDescriptor mFd;
+ private final long mStartOffset;
+ private final long mLength;
+
+ /**
+ * Create a new AssetFileDescriptor from the given values.
+ * @param fd The underlying file descriptor.
+ * @param startOffset The location within the file that the asset starts.
+ * This must be 0 if length is UNKNOWN_LENGTH.
+ * @param length The number of bytes of the asset, or
+ * {@link #UNKNOWN_LENGTH if it extends to the end of the file.
+ */
+ public AssetFileDescriptor(ParcelFileDescriptor fd, long startOffset,
+ long length) {
+ if (length < 0 && startOffset != 0) {
+ throw new IllegalArgumentException(
+ "startOffset must be 0 when using UNKNOWN_LENGTH");
+ }
+ mFd = fd;
+ mStartOffset = startOffset;
+ mLength = length;
+ }
+
+ /**
+ * The AssetFileDescriptor contains its own ParcelFileDescriptor, which
+ * in addition to the normal FileDescriptor object also allows you to close
+ * the descriptor when you are done with it.
+ */
+ public ParcelFileDescriptor getParcelFileDescriptor() {
+ return mFd;
+ }
+
+ /**
+ * Returns the FileDescriptor that can be used to read the data in the
+ * file.
+ */
+ public FileDescriptor getFileDescriptor() {
+ return mFd.getFileDescriptor();
+ }
+
+ /**
+ * Returns the byte offset where this asset entry's data starts.
+ */
+ public long getStartOffset() {
+ return mStartOffset;
+ }
+
+ /**
+ * Returns the total number of bytes of this asset entry's data. May be
+ * {@link #UNKNOWN_LENGTH} if the asset extends to the end of the file.
+ * If the AssetFileDescriptor was constructed with {@link #UNKNOWN_LENGTH},
+ * this will use {@link ParcelFileDescriptor#getStatSize()
+ * ParcelFileDescriptor.getStatSize()} to find the total size of the file,
+ * returning that number if found or {@link #UNKNOWN_LENGTH} if it could
+ * not be determined.
+ *
+ * @see #getDeclaredLength()
+ */
+ public long getLength() {
+ if (mLength >= 0) {
+ return mLength;
+ }
+ long len = mFd.getStatSize();
+ return len >= 0 ? len : UNKNOWN_LENGTH;
+ }
+
+ /**
+ * Return the actual number of bytes that were declared when the
+ * AssetFileDescriptor was constructed. Will be
+ * {@link #UNKNOWN_LENGTH} if the length was not declared, meaning data
+ * should be read to the end of the file.
+ *
+ * @see #getDeclaredLength()
+ */
+ public long getDeclaredLength() {
+ return mLength;
+ }
+
+ /**
+ * Convenience for calling <code>getParcelFileDescriptor().close()</code>.
+ */
+ public void close() throws IOException {
+ mFd.close();
+ }
+
+ /**
+ * Create and return a new auto-close input stream for this asset. This
+ * will either return a full asset {@link AutoCloseInputStream}, or
+ * an underlying {@link ParcelFileDescriptor.AutoCloseInputStream
+ * ParcelFileDescriptor.AutoCloseInputStream} depending on whether the
+ * the object represents a complete file or sub-section of a file. You
+ * should only call this once for a particular asset.
+ */
+ public FileInputStream createInputStream() throws IOException {
+ if (mLength < 0) {
+ return new ParcelFileDescriptor.AutoCloseInputStream(mFd);
+ }
+ return new AutoCloseInputStream(this);
+ }
+
+ /**
+ * Create and return a new auto-close output stream for this asset. This
+ * will either return a full asset {@link AutoCloseOutputStream}, or
+ * an underlying {@link ParcelFileDescriptor.AutoCloseOutputStream
+ * ParcelFileDescriptor.AutoCloseOutputStream} depending on whether the
+ * the object represents a complete file or sub-section of a file. You
+ * should only call this once for a particular asset.
+ */
+ public FileOutputStream createOutputStream() throws IOException {
+ if (mLength < 0) {
+ return new ParcelFileDescriptor.AutoCloseOutputStream(mFd);
+ }
+ return new AutoCloseOutputStream(this);
+ }
+
+ @Override
+ public String toString() {
+ return "{AssetFileDescriptor: " + mFd
+ + " start=" + mStartOffset + " len=" + mLength + "}";
+ }
+
+ /**
+ * An InputStream you can create on a ParcelFileDescriptor, which will
+ * take care of calling {@link ParcelFileDescriptor#close
+ * ParcelFileDescritor.close()} for you when the stream is closed.
+ */
+ public static class AutoCloseInputStream
+ extends ParcelFileDescriptor.AutoCloseInputStream {
+ private long mRemaining;
+
+ public AutoCloseInputStream(AssetFileDescriptor fd) throws IOException {
+ super(fd.getParcelFileDescriptor());
+ super.skip(fd.getStartOffset());
+ mRemaining = (int)fd.getLength();
+ }
+
+ @Override
+ public int available() throws IOException {
+ return mRemaining >= 0
+ ? (mRemaining < 0x7fffffff ? (int)mRemaining : 0x7fffffff)
+ : super.available();
+ }
+
+ @Override
+ public int read() throws IOException {
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return -1;
+ int res = super.read();
+ if (res >= 0) mRemaining--;
+ return res;
+ }
+
+ return super.read();
+ }
+
+ @Override
+ public int read(byte[] buffer, int offset, int count) throws IOException {
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return -1;
+ if (count > mRemaining) count = (int)mRemaining;
+ int res = super.read(buffer, offset, count);
+ if (res >= 0) mRemaining -= res;
+ return res;
+ }
+
+ return super.read(buffer, offset, count);
+ }
+
+ @Override
+ public int read(byte[] buffer) throws IOException {
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return -1;
+ int count = buffer.length;
+ if (count > mRemaining) count = (int)mRemaining;
+ int res = super.read(buffer, 0, count);
+ if (res >= 0) mRemaining -= res;
+ return res;
+ }
+
+ return super.read(buffer);
+ }
+
+ @Override
+ public long skip(long count) throws IOException {
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return -1;
+ if (count > mRemaining) count = mRemaining;
+ long res = super.skip(count);
+ if (res >= 0) mRemaining -= res;
+ return res;
+ }
+
+ // TODO Auto-generated method stub
+ return super.skip(count);
+ }
+
+ @Override
+ public void mark(int readlimit) {
+ if (mRemaining >= 0) {
+ // Not supported.
+ return;
+ }
+ super.mark(readlimit);
+ }
+
+ @Override
+ public boolean markSupported() {
+ if (mRemaining >= 0) {
+ return false;
+ }
+ return super.markSupported();
+ }
+
+ @Override
+ public synchronized void reset() throws IOException {
+ if (mRemaining >= 0) {
+ // Not supported.
+ return;
+ }
+ super.reset();
+ }
+ }
+
+ /**
+ * An OutputStream you can create on a ParcelFileDescriptor, which will
+ * take care of calling {@link ParcelFileDescriptor#close
+ * ParcelFileDescritor.close()} for you when the stream is closed.
+ */
+ public static class AutoCloseOutputStream
+ extends ParcelFileDescriptor.AutoCloseOutputStream {
+ private long mRemaining;
+
+ public AutoCloseOutputStream(AssetFileDescriptor fd) throws IOException {
+ super(fd.getParcelFileDescriptor());
+ if (fd.getParcelFileDescriptor().seekTo(fd.getStartOffset()) < 0) {
+ throw new IOException("Unable to seek");
+ }
+ mRemaining = (int)fd.getLength();
+ }
+
+ @Override
+ public void write(byte[] buffer, int offset, int count) throws IOException {
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return;
+ if (count > mRemaining) count = (int)mRemaining;
+ super.write(buffer, offset, count);
+ mRemaining -= count;
+ return;
+ }
+
+ super.write(buffer, offset, count);
+ }
+
+ @Override
+ public void write(byte[] buffer) throws IOException {
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return;
+ int count = buffer.length;
+ if (count > mRemaining) count = (int)mRemaining;
+ super.write(buffer);
+ mRemaining -= count;
+ return;
+ }
+
+ super.write(buffer);
+ }
+
+ @Override
+ public void write(int oneByte) throws IOException {
+ if (mRemaining >= 0) {
+ if (mRemaining == 0) return;
+ super.write(oneByte);
+ mRemaining--;
+ return;
+ }
+
+ super.write(oneByte);
+ }
+ }
+
+
+ /* Parcelable interface */
+ public int describeContents() {
+ return mFd.describeContents();
+ }
+
+ public void writeToParcel(Parcel out, int flags) {
+ mFd.writeToParcel(out, flags);
+ out.writeLong(mStartOffset);
+ out.writeLong(mLength);
+ }
+
+ AssetFileDescriptor(Parcel src) {
+ mFd = ParcelFileDescriptor.CREATOR.createFromParcel(src);
+ mStartOffset = src.readLong();
+ mLength = src.readLong();
+ }
+
+ public static final Parcelable.Creator<AssetFileDescriptor> CREATOR
+ = new Parcelable.Creator<AssetFileDescriptor>() {
+ public AssetFileDescriptor createFromParcel(Parcel in) {
+ return new AssetFileDescriptor(in);
+ }
+ public AssetFileDescriptor[] newArray(int size) {
+ return new AssetFileDescriptor[size];
+ }
+ };
+}
diff --git a/core/java/android/content/res/AssetManager.java b/core/java/android/content/res/AssetManager.java
new file mode 100644
index 0000000..fadcb35
--- /dev/null
+++ b/core/java/android/content/res/AssetManager.java
@@ -0,0 +1,697 @@
+/*
+ * Copyright (C) 2006 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 android.os.ParcelFileDescriptor;
+import android.util.Config;
+import android.util.Log;
+import android.util.TypedValue;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.Locale;
+
+/**
+ * Provides access to an application's raw asset files; see {@link Resources}
+ * for the way most applications will want to retrieve their resource data.
+ * This class presents a lower-level API that allows you to open and read raw
+ * files that have been bundled with the application as a simple stream of
+ * bytes.
+ */
+public final class AssetManager {
+ /* modes used when opening an asset */
+
+ /**
+ * Mode for {@link #open(String, int)}: no specific information about how
+ * data will be accessed.
+ */
+ public static final int ACCESS_UNKNOWN = 0;
+ /**
+ * Mode for {@link #open(String, int)}: Read chunks, and seek forward and
+ * backward.
+ */
+ public static final int ACCESS_RANDOM = 1;
+ /**
+ * Mode for {@link #open(String, int)}: Read sequentially, with an
+ * occasional forward seek.
+ */
+ public static final int ACCESS_STREAMING = 2;
+ /**
+ * Mode for {@link #open(String, int)}: Attempt to load contents into
+ * memory, for fast small reads.
+ */
+ public static final int ACCESS_BUFFER = 3;
+
+ private static final String TAG = "AssetManager";
+ private static final boolean localLOGV = Config.LOGV || false;
+
+ private static final Object mSync = new Object();
+ private static final TypedValue mValue = new TypedValue();
+ private static final long[] mOffsets = new long[2];
+ private static AssetManager mSystem = null;
+
+ // For communication with native code.
+ private int mObject;
+
+ private StringBlock mStringBlocks[] = null;
+
+ private int mNumRefs = 1;
+ private boolean mOpen = true;
+ private String mAssetDir;
+ private String mAppName;
+
+ /**
+ * Create a new AssetManager containing only the basic system assets.
+ * Applications will not generally use this method, instead retrieving the
+ * appropriate asset manager with {@link Resources#getAssets}. Not for
+ * use by applications.
+ * {@hide}
+ */
+ public AssetManager() {
+ synchronized (mSync) {
+ init();
+ if (localLOGV) Log.v(TAG, "New asset manager: " + this);
+ ensureSystemAssets();
+ }
+ }
+
+ private static void ensureSystemAssets() {
+ synchronized (mSync) {
+ if (mSystem == null) {
+ AssetManager system = new AssetManager(true);
+ system.makeStringBlocks(false);
+ mSystem = system;
+ }
+ }
+ }
+
+ private AssetManager(boolean isSystem) {
+ init();
+ if (localLOGV) Log.v(TAG, "New asset manager: " + this);
+ }
+
+ /**
+ * Return a global shared asset manager that provides access to only
+ * system assets (no application assets).
+ * {@hide}
+ */
+ public static AssetManager getSystem() {
+ ensureSystemAssets();
+ return mSystem;
+ }
+
+ /**
+ * Close this asset manager.
+ */
+ public void close() {
+ synchronized(mSync) {
+ //System.out.println("Release: num=" + mNumRefs
+ // + ", released=" + mReleased);
+ if (mOpen) {
+ mOpen = false;
+ decRefsLocked();
+ }
+ }
+ }
+
+ /**
+ * Retrieve the string value associated with a particular resource
+ * identifier for the current configuration / skin.
+ */
+ /*package*/ final CharSequence getResourceText(int ident) {
+ synchronized (mSync) {
+ TypedValue tmpValue = mValue;
+ int block = loadResourceValue(ident, tmpValue, true);
+ if (block >= 0) {
+ if (tmpValue.type == TypedValue.TYPE_STRING) {
+ return mStringBlocks[block].get(tmpValue.data);
+ }
+ return tmpValue.coerceToString();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve the string value associated with a particular resource
+ * identifier for the current configuration / skin.
+ */
+ /*package*/ final CharSequence getResourceBagText(int ident, int bagEntryId) {
+ synchronized (mSync) {
+ TypedValue tmpValue = mValue;
+ int block = loadResourceBagValue(ident, bagEntryId, tmpValue, true);
+ if (block >= 0) {
+ if (tmpValue.type == TypedValue.TYPE_STRING) {
+ return mStringBlocks[block].get(tmpValue.data);
+ }
+ return tmpValue.coerceToString();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Retrieve the string array associated with a particular resource
+ * identifier.
+ * @param id Resource id of the string array
+ */
+ /*package*/ final String[] getResourceStringArray(final int id) {
+ String[] retArray = getArrayStringResource(id);
+ return retArray;
+ }
+
+
+ /*package*/ final boolean getResourceValue(int ident,
+ TypedValue outValue,
+ boolean resolveRefs)
+ {
+ int block = loadResourceValue(ident, outValue, resolveRefs);
+ if (block >= 0) {
+ if (outValue.type != TypedValue.TYPE_STRING) {
+ return true;
+ }
+ outValue.string = mStringBlocks[block].get(outValue.data);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Retrieve the text array associated with a particular resource
+ * identifier.
+ * @param id Resource id of the string array
+ */
+ /*package*/ final CharSequence[] getResourceTextArray(final int id) {
+ int[] rawInfoArray = getArrayStringInfo(id);
+ int rawInfoArrayLen = rawInfoArray.length;
+ final int infoArrayLen = rawInfoArrayLen / 2;
+ int block;
+ int index;
+ CharSequence[] retArray = new CharSequence[infoArrayLen];
+ for (int i = 0, j = 0; i < rawInfoArrayLen; i = i + 2, j++) {
+ block = rawInfoArray[i];
+ index = rawInfoArray[i + 1];
+ retArray[j] = index >= 0 ? mStringBlocks[block].get(index) : null;
+ }
+ return retArray;
+ }
+
+ /*package*/ final boolean getThemeValue(int theme, int ident,
+ TypedValue outValue, boolean resolveRefs) {
+ int block = loadThemeAttributeValue(theme, ident, outValue, resolveRefs);
+ if (block >= 0) {
+ if (outValue.type != TypedValue.TYPE_STRING) {
+ return true;
+ }
+ StringBlock[] blocks = mStringBlocks;
+ if (blocks == null) {
+ ensureStringBlocks();
+ }
+ outValue.string = blocks[block].get(outValue.data);
+ return true;
+ }
+ return false;
+ }
+
+ /*package*/ final void ensureStringBlocks() {
+ if (mStringBlocks == null) {
+ synchronized (mSync) {
+ if (mStringBlocks == null) {
+ makeStringBlocks(true);
+ }
+ }
+ }
+ }
+
+ private final void makeStringBlocks(boolean copyFromSystem) {
+ final int sysNum = copyFromSystem ? mSystem.mStringBlocks.length : 0;
+ final int num = getStringBlockCount();
+ mStringBlocks = new StringBlock[num];
+ if (localLOGV) Log.v(TAG, "Making string blocks for " + this
+ + ": " + num);
+ for (int i=0; i<num; i++) {
+ if (i < sysNum) {
+ mStringBlocks[i] = mSystem.mStringBlocks[i];
+ } else {
+ mStringBlocks[i] = new StringBlock(getNativeStringBlock(i), true);
+ }
+ }
+ }
+
+ /*package*/ final CharSequence getPooledString(int block, int id) {
+ //System.out.println("Get pooled: block=" + block
+ // + ", id=#" + Integer.toHexString(id)
+ // + ", blocks=" + mStringBlocks);
+ return mStringBlocks[block-1].get(id);
+ }
+
+ /**
+ * Open an asset using ACCESS_STREAMING mode. This provides access to
+ * files that have been bundled with an application as assets -- that is,
+ * files placed in to the "assets" directory.
+ *
+ * @param fileName The name of the asset to open. This name can be
+ * hierarchical.
+ *
+ * @see #open(String, int)
+ * @see #list
+ */
+ public final InputStream open(String fileName) throws IOException {
+ return open(fileName, ACCESS_STREAMING);
+ }
+
+ /**
+ * Open an asset using an explicit access mode, returning an InputStream to
+ * read its contents. This provides access to files that have been bundled
+ * with an application as assets -- that is, files placed in to the
+ * "assets" directory.
+ *
+ * @param fileName The name of the asset to open. This name can be
+ * hierarchical.
+ * @param accessMode Desired access mode for retrieving the data.
+ *
+ * @see #ACCESS_UNKNOWN
+ * @see #ACCESS_STREAMING
+ * @see #ACCESS_RANDOM
+ * @see #ACCESS_BUFFER
+ * @see #open(String)
+ * @see #list
+ */
+ public final InputStream open(String fileName, int accessMode)
+ throws IOException {
+ synchronized (mSync) {
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
+ }
+ int asset = openAsset(fileName, accessMode);
+ if (asset != 0) {
+ mNumRefs++;
+ return new AssetInputStream(asset);
+ }
+ }
+ throw new FileNotFoundException("Asset file: " + fileName);
+ }
+
+ public final AssetFileDescriptor openFd(String fileName)
+ throws IOException {
+ synchronized (mSync) {
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
+ }
+ ParcelFileDescriptor pfd = openAssetFd(fileName, mOffsets);
+ if (pfd != null) {
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ }
+ }
+ throw new FileNotFoundException("Asset file: " + fileName);
+ }
+
+ /**
+ * Return a String array of all the assets at the given path.
+ *
+ * @param path A relative path within the assets, i.e., "docs/home.html".
+ *
+ * @return String[] Array of strings, one for each asset. These file
+ * names are relative to 'path'. You can open the file by
+ * concatenating 'path' and a name in the returned string (via
+ * File) and passing that to open().
+ *
+ * @see #open
+ */
+ public native final String[] list(String path)
+ throws IOException;
+
+ /**
+ * {@hide}
+ * Open a non-asset file as an asset using ACCESS_STREAMING mode. This
+ * provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use
+ * this.
+ *
+ * @see #open(String)
+ */
+ public final InputStream openNonAsset(String fileName) throws IOException {
+ return openNonAsset(0, fileName, ACCESS_STREAMING);
+ }
+
+ /**
+ * {@hide}
+ * Open a non-asset file as an asset using a specific access mode. This
+ * provides direct access to all of the files included in an application
+ * package (not only its assets). Applications should not normally use
+ * this.
+ *
+ * @see #open(String, int)
+ */
+ public final InputStream openNonAsset(String fileName, int accessMode)
+ throws IOException {
+ return openNonAsset(0, fileName, accessMode);
+ }
+
+ /**
+ * {@hide}
+ * Open a non-asset in a specified package. Not for use by applications.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName Name of the asset to retrieve.
+ */
+ public final InputStream openNonAsset(int cookie, String fileName)
+ throws IOException {
+ return openNonAsset(cookie, fileName, ACCESS_STREAMING);
+ }
+
+ /**
+ * {@hide}
+ * Open a non-asset in a specified package. Not for use by applications.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName Name of the asset to retrieve.
+ * @param accessMode Desired access mode for retrieving the data.
+ */
+ public final InputStream openNonAsset(int cookie, String fileName, int accessMode)
+ throws IOException {
+ synchronized (mSync) {
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
+ }
+ int asset = openNonAssetNative(cookie, fileName, accessMode);
+ if (asset != 0) {
+ mNumRefs++;
+ return new AssetInputStream(asset);
+ }
+ }
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
+ }
+
+ public final AssetFileDescriptor openNonAssetFd(String fileName)
+ throws IOException {
+ return openNonAssetFd(0, fileName);
+ }
+
+ public final AssetFileDescriptor openNonAssetFd(int cookie,
+ String fileName) throws IOException {
+ synchronized (mSync) {
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
+ }
+ ParcelFileDescriptor pfd = openNonAssetFdNative(cookie,
+ fileName, mOffsets);
+ if (pfd != null) {
+ return new AssetFileDescriptor(pfd, mOffsets[0], mOffsets[1]);
+ }
+ }
+ throw new FileNotFoundException("Asset absolute file: " + fileName);
+ }
+
+ /**
+ * Retrieve a parser for a compiled XML file.
+ *
+ * @param fileName The name of the file to retrieve.
+ */
+ public final XmlResourceParser openXmlResourceParser(String fileName)
+ throws IOException {
+ return openXmlResourceParser(0, fileName);
+ }
+
+ /**
+ * Retrieve a parser for a compiled XML file.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName The name of the file to retrieve.
+ */
+ public final XmlResourceParser openXmlResourceParser(int cookie,
+ String fileName) throws IOException {
+ XmlBlock block = openXmlBlockAsset(cookie, fileName);
+ XmlResourceParser rp = block.newParser();
+ block.close();
+ return rp;
+ }
+
+ /**
+ * {@hide}
+ * Retrieve a non-asset as a compiled XML file. Not for use by
+ * applications.
+ *
+ * @param fileName The name of the file to retrieve.
+ */
+ /*package*/ final XmlBlock openXmlBlockAsset(String fileName)
+ throws IOException {
+ return openXmlBlockAsset(0, fileName);
+ }
+
+ /**
+ * {@hide}
+ * Retrieve a non-asset as a compiled XML file. Not for use by
+ * applications.
+ *
+ * @param cookie Identifier of the package to be opened.
+ * @param fileName Name of the asset to retrieve.
+ */
+ /*package*/ final XmlBlock openXmlBlockAsset(int cookie, String fileName)
+ throws IOException {
+ synchronized (mSync) {
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
+ }
+ int xmlBlock = openXmlAssetNative(cookie, fileName);
+ if (xmlBlock != 0) {
+ mNumRefs++;
+ return new XmlBlock(this, xmlBlock);
+ }
+ }
+ throw new FileNotFoundException("Asset XML file: " + fileName);
+ }
+
+ /*package*/ void xmlBlockGone() {
+ synchronized (mSync) {
+ decRefsLocked();
+ }
+ }
+
+ /*package*/ final int createTheme() {
+ synchronized (mSync) {
+ if (!mOpen) {
+ throw new RuntimeException("Assetmanager has been closed");
+ }
+ mNumRefs++;
+ return newTheme();
+ }
+ }
+
+ /*package*/ final void releaseTheme(int theme) {
+ synchronized (mSync) {
+ deleteTheme(theme);
+ decRefsLocked();
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ destroy();
+ }
+
+ public final class AssetInputStream extends InputStream {
+ public final int getAssetInt() {
+ return mAsset;
+ }
+ private AssetInputStream(int asset)
+ {
+ mAsset = asset;
+ mLength = getAssetLength(asset);
+ }
+ public final int read() throws IOException {
+ return readAssetChar(mAsset);
+ }
+ public final boolean markSupported() {
+ return true;
+ }
+ public final int available() throws IOException {
+ long len = getAssetRemainingLength(mAsset);
+ return len > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)len;
+ }
+ public final void close() throws IOException {
+ synchronized (AssetManager.mSync) {
+ if (mAsset != 0) {
+ destroyAsset(mAsset);
+ mAsset = 0;
+ decRefsLocked();
+ }
+ }
+ }
+ public final void mark(int readlimit) {
+ mMarkPos = seekAsset(mAsset, 0, 0);
+ }
+ public final void reset() throws IOException {
+ seekAsset(mAsset, mMarkPos, -1);
+ }
+ public final int read(byte[] b) throws IOException {
+ return readAsset(mAsset, b, 0, b.length);
+ }
+ public final int read(byte[] b, int off, int len) throws IOException {
+ return readAsset(mAsset, b, off, len);
+ }
+ public final long skip(long n) throws IOException {
+ long pos = seekAsset(mAsset, 0, 0);
+ if ((pos+n) > mLength) {
+ n = mLength-pos;
+ }
+ if (n > 0) {
+ seekAsset(mAsset, n, 0);
+ }
+ return n;
+ }
+
+ protected void finalize() throws Throwable
+ {
+ close();
+ }
+
+ private int mAsset;
+ private long mLength;
+ private long mMarkPos;
+ }
+
+ /**
+ * Add an additional set of assets to the asset manager. This can be
+ * either a directory or ZIP file. Not for use by applications. A
+ * zero return value indicates failure.
+ * {@hide}
+ */
+ public native final int addAssetPath(String path);
+
+ /**
+ * Determine whether the state in this asset manager is up-to-date with
+ * the files on the filesystem. If false is returned, you need to
+ * instantiate a new AssetManager class to see the new data.
+ * {@hide}
+ */
+ public native final boolean isUpToDate();
+
+ /**
+ * Change the locale being used by this asset manager. Not for use by
+ * applications.
+ * {@hide}
+ */
+ public native final void setLocale(String locale);
+
+ /**
+ * Get the locales that this asset manager contains data for.
+ */
+ public native final String[] getLocales();
+
+ /**
+ * Change the configuation used when retrieving resources. Not for use by
+ * applications.
+ * {@hide}
+ */
+ public native final void setConfiguration(int mcc, int mnc, String locale,
+ int orientation, int touchscreen, int density, int keyboard,
+ int keyboardHidden, int navigation, int screenWidth, int screenHeight,
+ int majorVersion);
+
+ /**
+ * Retrieve the resource identifier for the given resource name.
+ */
+ /*package*/ native final int getResourceIdentifier(String type,
+ String name,
+ String defPackage);
+
+ /*package*/ native final String getResourceName(int resid);
+ /*package*/ native final String getResourcePackageName(int resid);
+ /*package*/ native final String getResourceTypeName(int resid);
+ /*package*/ native final String getResourceEntryName(int resid);
+
+ private native final int openAsset(String fileName, int accessMode);
+ private final native ParcelFileDescriptor openAssetFd(String fileName,
+ long[] outOffsets) throws IOException;
+ private native final int openNonAssetNative(int cookie, String fileName,
+ int accessMode);
+ private native ParcelFileDescriptor openNonAssetFdNative(int cookie,
+ String fileName, long[] outOffsets) throws IOException;
+ private native final void destroyAsset(int asset);
+ private native final int readAssetChar(int asset);
+ private native final int readAsset(int asset, byte[] b, int off, int len);
+ private native final long seekAsset(int asset, long offset, int whence);
+ private native final long getAssetLength(int asset);
+ private native final long getAssetRemainingLength(int asset);
+
+ /** Returns true if the resource was found, filling in mRetStringBlock and
+ * mRetData. */
+ private native final int loadResourceValue(int ident, TypedValue outValue,
+ boolean resolve);
+ /** Returns true if the resource was found, filling in mRetStringBlock and
+ * mRetData. */
+ private native final int loadResourceBagValue(int ident, int bagEntryId, TypedValue outValue,
+ boolean resolve);
+ /*package*/ static final int STYLE_NUM_ENTRIES = 5;
+ /*package*/ static final int STYLE_TYPE = 0;
+ /*package*/ static final int STYLE_DATA = 1;
+ /*package*/ static final int STYLE_ASSET_COOKIE = 2;
+ /*package*/ static final int STYLE_RESOURCE_ID = 3;
+ /*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
+ /*package*/ native static final boolean applyStyle(int theme,
+ int defStyleAttr, int defStyleRes, int xmlParser,
+ int[] inAttrs, int[] outValues, int[] outIndices);
+ /*package*/ native final boolean retrieveAttributes(
+ int xmlParser, int[] inAttrs, int[] outValues, int[] outIndices);
+ /*package*/ native final int getArraySize(int resource);
+ /*package*/ native final int retrieveArray(int resource, int[] outValues);
+ private native final int getStringBlockCount();
+ private native final int getNativeStringBlock(int block);
+
+ /**
+ * {@hide}
+ */
+ public native final String getCookieName(int cookie);
+
+ /**
+ * {@hide}
+ */
+ public native static final int getGlobalAssetCount();
+
+ /**
+ * {@hide}
+ */
+ public native static final int getGlobalAssetManagerCount();
+
+ private native final int newTheme();
+ private native final void deleteTheme(int theme);
+ /*package*/ native static final void applyThemeStyle(int theme, int styleRes, boolean force);
+ /*package*/ native static final void copyTheme(int dest, int source);
+ /*package*/ native static final int loadThemeAttributeValue(int theme, int ident,
+ TypedValue outValue,
+ boolean resolve);
+ /*package*/ native static final void dumpTheme(int theme, int priority, String tag, String prefix);
+
+ private native final int openXmlAssetNative(int cookie, String fileName);
+
+ private native final String[] getArrayStringResource(int arrayRes);
+ private native final int[] getArrayStringInfo(int arrayRes);
+ /*package*/ native final int[] getArrayIntResource(int arrayRes);
+
+ private native final void init();
+ private native final void destroy();
+
+ private final void decRefsLocked() {
+ mNumRefs--;
+ //System.out.println("Dec streams: mNumRefs=" + mNumRefs
+ // + " mReleased=" + mReleased);
+ if (mNumRefs == 0) {
+ destroy();
+ }
+ }
+}
diff --git a/core/java/android/content/res/ColorStateList.java b/core/java/android/content/res/ColorStateList.java
new file mode 100644
index 0000000..0f3f270
--- /dev/null
+++ b/core/java/android/content/res/ColorStateList.java
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2007 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.internal.util.ArrayUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.util.AttributeSet;
+import android.util.SparseArray;
+import android.util.StateSet;
+import android.util.Xml;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Arrays;
+
+/**
+ *
+ * Lets you map {@link android.view.View} state sets to colors.
+ *
+ * {@link android.content.res.ColorStateList}s are created from XML resource files defined in the
+ * "color" subdirectory directory of an application's resource directory. The XML file contains
+ * a single "selector" element with a number of "item" elements inside. For example:
+ *
+ * <pre>
+ * <selector xmlns:android="http://schemas.android.com/apk/res/android">
+ * <item android:state_focused="true" android:color="@color/testcolor1"/>
+ * <item android:state_pressed="true" android:state_enabled="false" android:color="@color/testcolor2" />
+ * <item android:state_enabled="false" android:colore="@color/testcolor3" />
+ * <item android:state_active="true" android:color="@color/testcolor4" />
+ * <item android:color="@color/testcolor5"/>
+ * </selector>
+ * </pre>
+ *
+ * This defines a set of state spec / color pairs where each state spec specifies a set of
+ * states that a view must either be in or not be in and the color specifies the color associated
+ * with that spec. The list of state specs will be processed in order of the items in the XML file.
+ * An item with no state spec is considered to match any set of states and is generally useful as
+ * a final item to be used as a default. Note that if you have such an item before any other items
+ * in the list then any subsequent items will end up being ignored.
+ */
+public class ColorStateList implements Parcelable {
+
+ private int[][] mStateSpecs; // must be parallel to mColors
+ private int[] mColors; // must be parallel to mStateSpecs
+ private int mDefaultColor = 0xffff0000;
+
+ private static final int[][] EMPTY = new int[][] { new int[0] };
+ private static final SparseArray<WeakReference<ColorStateList>> sCache =
+ new SparseArray<WeakReference<ColorStateList>>();
+
+ private ColorStateList() { }
+
+ /**
+ * Creates a ColorStateList that returns the specified mapping from
+ * states to colors.
+ */
+ public ColorStateList(int[][] states, int[] colors) {
+ mStateSpecs = states;
+ mColors = colors;
+
+ if (states.length > 0) {
+ mDefaultColor = colors[0];
+
+ for (int i = 0; i < states.length; i++) {
+ if (states[i].length == 0) {
+ mDefaultColor = colors[i];
+ }
+ }
+ }
+ }
+
+ /**
+ * Creates or retrieves a ColorStateList that always returns a single color.
+ */
+ public static ColorStateList valueOf(int color) {
+ // TODO: should we collect these eventually?
+ synchronized (sCache) {
+ WeakReference<ColorStateList> ref = sCache.get(color);
+ ColorStateList csl = ref != null ? ref.get() : null;
+
+ if (csl != null) {
+ return csl;
+ }
+
+ csl = new ColorStateList(EMPTY, new int[] { color });
+ sCache.put(color, new WeakReference<ColorStateList>(csl));
+ return csl;
+ }
+ }
+
+ /**
+ * Create a ColorStateList from an XML document, given a set of {@link Resources}.
+ */
+ public static ColorStateList createFromXml(Resources r, XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+
+ AttributeSet attrs = Xml.asAttributeSet(parser);
+
+ int type;
+ while ((type=parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT) {
+ }
+
+ if (type != XmlPullParser.START_TAG) {
+ throw new XmlPullParserException("No start tag found");
+ }
+
+ return createFromXmlInner(r, parser, attrs);
+ }
+
+ /* Create from inside an XML document. Called on a parser positioned at
+ * a tag in an XML document, tries to create a ColorStateList from that tag.
+ * Returns null if the tag is not a valid ColorStateList.
+ */
+ private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser,
+ AttributeSet attrs) throws XmlPullParserException, IOException {
+
+ ColorStateList colorStateList;
+
+ final String name = parser.getName();
+
+ if (name.equals("selector")) {
+ colorStateList = new ColorStateList();
+ } else {
+ throw new XmlPullParserException(
+ parser.getPositionDescription() + ": invalid drawable tag " + name);
+ }
+
+ colorStateList.inflate(r, parser, attrs);
+ return colorStateList;
+ }
+
+ /**
+ * Creates a new ColorStateList that has the same states and
+ * colors as this one but where each color has the specified alpha value
+ * (0-255).
+ */
+ public ColorStateList withAlpha(int alpha) {
+ int[] colors = new int[mColors.length];
+
+ int len = colors.length;
+ for (int i = 0; i < len; i++) {
+ colors[i] = (mColors[i] & 0xFFFFFF) | (alpha << 24);
+ }
+
+ return new ColorStateList(mStateSpecs, colors);
+ }
+
+ /**
+ * Fill in this object based on the contents of an XML "selector" element.
+ */
+ private void inflate(Resources r, XmlPullParser parser, AttributeSet attrs)
+ throws XmlPullParserException, IOException {
+
+ int type;
+
+ final int innerDepth = parser.getDepth()+1;
+ int depth;
+
+ int listAllocated = 20;
+ int listSize = 0;
+ int[] colorList = new int[listAllocated];
+ int[][] stateSpecList = new int[listAllocated][];
+
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && ((depth=parser.getDepth()) >= innerDepth
+ || type != XmlPullParser.END_TAG)) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+
+ if (depth > innerDepth || !parser.getName().equals("item")) {
+ continue;
+ }
+
+ int colorRes = 0;
+ int color = 0xffff0000;
+ boolean haveColor = false;
+
+ int i;
+ int j = 0;
+ final int numAttrs = attrs.getAttributeCount();
+ int[] stateSpec = new int[numAttrs];
+ for (i = 0; i < numAttrs; i++) {
+ final int stateResId = attrs.getAttributeNameResource(i);
+ if (stateResId == 0) break;
+ if (stateResId == com.android.internal.R.attr.color) {
+ colorRes = attrs.getAttributeResourceValue(i, 0);
+
+ if (colorRes == 0) {
+ color = attrs.getAttributeIntValue(i, color);
+ haveColor = true;
+ }
+ } else {
+ stateSpec[j++] = attrs.getAttributeBooleanValue(i, false)
+ ? stateResId
+ : -stateResId;
+ }
+ }
+ stateSpec = StateSet.trimStateSet(stateSpec, j);
+
+ if (colorRes != 0) {
+ color = r.getColor(colorRes);
+ } else if (!haveColor) {
+ throw new XmlPullParserException(
+ parser.getPositionDescription()
+ + ": <item> tag requires a 'android:color' attribute.");
+ }
+
+ if (listSize == 0 || stateSpec.length == 0) {
+ mDefaultColor = color;
+ }
+
+ if (listSize + 1 >= listAllocated) {
+ listAllocated = ArrayUtils.idealIntArraySize(listSize + 1);
+
+ int[] ncolor = new int[listAllocated];
+ System.arraycopy(colorList, 0, ncolor, 0, listSize);
+
+ int[][] nstate = new int[listAllocated][];
+ System.arraycopy(stateSpecList, 0, nstate, 0, listSize);
+
+ colorList = ncolor;
+ stateSpecList = nstate;
+ }
+
+ colorList[listSize] = color;
+ stateSpecList[listSize] = stateSpec;
+ listSize++;
+ }
+
+ mColors = new int[listSize];
+ mStateSpecs = new int[listSize][];
+ System.arraycopy(colorList, 0, mColors, 0, listSize);
+ System.arraycopy(stateSpecList, 0, mStateSpecs, 0, listSize);
+ }
+
+ public boolean isStateful() {
+ return mStateSpecs.length > 1;
+ }
+
+ /**
+ * Return the color associated with the given set of {@link android.view.View} states.
+ *
+ * @param stateSet an array of {@link android.view.View} states
+ * @param defaultColor the color to return if there's not state spec in this
+ * {@link ColorStateList} that matches the stateSet.
+ *
+ * @return the color associated with that set of states in this {@link ColorStateList}.
+ */
+ public int getColorForState(int[] stateSet, int defaultColor) {
+ final int setLength = mStateSpecs.length;
+ for (int i = 0; i < setLength; i++) {
+ int[] stateSpec = mStateSpecs[i];
+ if (StateSet.stateSetMatches(stateSpec, stateSet)) {
+ return mColors[i];
+ }
+ }
+ return defaultColor;
+ }
+
+ /**
+ * Return the default color in this {@link ColorStateList}.
+ *
+ * @return the default color in this {@link ColorStateList}.
+ */
+ public int getDefaultColor() {
+ return mDefaultColor;
+ }
+
+ public String toString() {
+ return "ColorStateList{" +
+ "mStateSpecs=" + Arrays.deepToString(mStateSpecs) +
+ "mColors=" + Arrays.toString(mColors) +
+ "mDefaultColor=" + mDefaultColor + '}';
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ final int N = mStateSpecs.length;
+ dest.writeInt(N);
+ for (int i=0; i<N; i++) {
+ dest.writeIntArray(mStateSpecs[i]);
+ }
+ dest.writeArray(mStateSpecs);
+ dest.writeIntArray(mColors);
+ }
+
+ public static final Parcelable.Creator<ColorStateList> CREATOR =
+ new Parcelable.Creator<ColorStateList>() {
+ public ColorStateList[] newArray(int size) {
+ return new ColorStateList[size];
+ }
+
+ public ColorStateList createFromParcel(Parcel source) {
+ final int N = source.readInt();
+ int[][] stateSpecs = new int[N][];
+ for (int i=0; i<N; i++) {
+ stateSpecs[i] = source.createIntArray();
+ }
+ int[] colors = source.createIntArray();
+ return new ColorStateList(stateSpecs, colors);
+ }
+ };
+}
diff --git a/core/java/android/content/res/Configuration.aidl b/core/java/android/content/res/Configuration.aidl
new file mode 100755
index 0000000..bb7f2dd
--- /dev/null
+++ b/core/java/android/content/res/Configuration.aidl
@@ -0,0 +1,21 @@
+/* //device/java/android/android/view/WindowManager.aidl
+**
+** Copyright 2007, 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;
+
+parcelable Configuration;
+
diff --git a/core/java/android/content/res/Configuration.java b/core/java/android/content/res/Configuration.java
new file mode 100644
index 0000000..7e4b7ac
--- /dev/null
+++ b/core/java/android/content/res/Configuration.java
@@ -0,0 +1,436 @@
+package android.content.res;
+
+import android.content.pm.ActivityInfo;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+/**
+ * This class describes all device configuration information that can
+ * impact the resources the application retrieves. This includes both
+ * user-specified configuration options (locale and scaling) as well
+ * as dynamic device configuration (various types of input devices).
+ */
+public final class Configuration implements Parcelable, Comparable<Configuration> {
+ /**
+ * Current user preference for the scaling factor for fonts, relative
+ * to the base density scaling.
+ */
+ public float fontScale;
+
+ /**
+ * IMSI MCC (Mobile Country Code). 0 if undefined.
+ */
+ public int mcc;
+
+ /**
+ * IMSI MNC (Mobile Network Code). 0 if undefined.
+ */
+ public int mnc;
+
+ /**
+ * Current user preference for the locale.
+ */
+ public Locale locale;
+
+ /**
+ * Locale should persist on setting
+ * @hide pending API council approval
+ */
+ public boolean userSetLocale;
+
+ public static final int TOUCHSCREEN_UNDEFINED = 0;
+ public static final int TOUCHSCREEN_NOTOUCH = 1;
+ public static final int TOUCHSCREEN_STYLUS = 2;
+ public static final int TOUCHSCREEN_FINGER = 3;
+
+ /**
+ * The kind of touch screen attached to the device.
+ * One of: {@link #TOUCHSCREEN_NOTOUCH}, {@link #TOUCHSCREEN_STYLUS},
+ * {@link #TOUCHSCREEN_FINGER}.
+ */
+ public int touchscreen;
+
+ public static final int KEYBOARD_UNDEFINED = 0;
+ public static final int KEYBOARD_NOKEYS = 1;
+ public static final int KEYBOARD_QWERTY = 2;
+ public static final int KEYBOARD_12KEY = 3;
+
+ /**
+ * The kind of keyboard attached to the device.
+ * One of: {@link #KEYBOARD_QWERTY}, {@link #KEYBOARD_12KEY}.
+ */
+ public int keyboard;
+
+ public static final int KEYBOARDHIDDEN_UNDEFINED = 0;
+ public static final int KEYBOARDHIDDEN_NO = 1;
+ public static final int KEYBOARDHIDDEN_YES = 2;
+ /** Constant matching actual resource implementation. {@hide} */
+ public static final int KEYBOARDHIDDEN_SOFT = 3;
+
+ /**
+ * A flag indicating whether any keyboard is available. Unlike
+ * {@link #hardKeyboardHidden}, this also takes into account a soft
+ * keyboard, so if the hard keyboard is hidden but there is soft
+ * keyboard available, it will be set to NO. Value is one of:
+ * {@link #KEYBOARDHIDDEN_NO}, {@link #KEYBOARDHIDDEN_YES}.
+ */
+ public int keyboardHidden;
+
+ public static final int HARDKEYBOARDHIDDEN_UNDEFINED = 0;
+ public static final int HARDKEYBOARDHIDDEN_NO = 1;
+ public static final int HARDKEYBOARDHIDDEN_YES = 2;
+
+ /**
+ * A flag indicating whether the hard keyboard has been hidden. This will
+ * be set on a device with a mechanism to hide the keyboard from the
+ * user, when that mechanism is closed. One of:
+ * {@link #HARDKEYBOARDHIDDEN_NO}, {@link #HARDKEYBOARDHIDDEN_YES}.
+ */
+ public int hardKeyboardHidden;
+
+ public static final int NAVIGATION_UNDEFINED = 0;
+ public static final int NAVIGATION_NONAV = 1;
+ public static final int NAVIGATION_DPAD = 2;
+ public static final int NAVIGATION_TRACKBALL = 3;
+ public static final int NAVIGATION_WHEEL = 4;
+
+ /**
+ * The kind of navigation method available on the device.
+ * One of: {@link #NAVIGATION_DPAD}, {@link #NAVIGATION_TRACKBALL},
+ * {@link #NAVIGATION_WHEEL}.
+ */
+ public int navigation;
+
+ public static final int ORIENTATION_UNDEFINED = 0;
+ public static final int ORIENTATION_PORTRAIT = 1;
+ public static final int ORIENTATION_LANDSCAPE = 2;
+ public static final int ORIENTATION_SQUARE = 3;
+
+ /**
+ * Overall orientation of the screen. May be one of
+ * {@link #ORIENTATION_LANDSCAPE}, {@link #ORIENTATION_PORTRAIT},
+ * or {@link #ORIENTATION_SQUARE}.
+ */
+ public int orientation;
+
+ /**
+ * Construct an invalid Configuration. You must call {@link #setToDefaults}
+ * for this object to be valid. {@more}
+ */
+ public Configuration() {
+ setToDefaults();
+ }
+
+ /**
+ * Makes a deep copy suitable for modification.
+ */
+ public Configuration(Configuration o) {
+ fontScale = o.fontScale;
+ mcc = o.mcc;
+ mnc = o.mnc;
+ if (o.locale != null) {
+ locale = (Locale) o.locale.clone();
+ }
+ userSetLocale = o.userSetLocale;
+ touchscreen = o.touchscreen;
+ keyboard = o.keyboard;
+ keyboardHidden = o.keyboardHidden;
+ hardKeyboardHidden = o.hardKeyboardHidden;
+ navigation = o.navigation;
+ orientation = o.orientation;
+ }
+
+ public String toString() {
+ return "{ scale=" + fontScale + " imsi=" + mcc + "/" + mnc
+ + " locale=" + locale
+ + " touch=" + touchscreen + " key=" + keyboard + "/"
+ + keyboardHidden + "/" + hardKeyboardHidden
+ + " nav=" + navigation + " orien=" + orientation + " }";
+ }
+
+ /**
+ * Set this object to the system defaults.
+ */
+ public void setToDefaults() {
+ fontScale = 1;
+ mcc = mnc = 0;
+ locale = Locale.getDefault();
+ userSetLocale = false;
+ touchscreen = TOUCHSCREEN_UNDEFINED;
+ keyboard = KEYBOARD_UNDEFINED;
+ keyboardHidden = KEYBOARDHIDDEN_UNDEFINED;
+ hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
+ navigation = NAVIGATION_UNDEFINED;
+ orientation = ORIENTATION_UNDEFINED;
+ }
+
+ /** {@hide} */
+ @Deprecated public void makeDefault() {
+ setToDefaults();
+ }
+
+ /**
+ * Copy the fields from delta into this Configuration object, keeping
+ * track of which ones have changed. Any undefined fields in
+ * <var>delta</var> are ignored and not copied in to the current
+ * Configuration.
+ * @return Returns a bit mask of the changed fields, as per
+ * {@link #diff}.
+ */
+ public int updateFrom(Configuration delta) {
+ int changed = 0;
+ if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+ changed |= ActivityInfo.CONFIG_FONT_SCALE;
+ fontScale = delta.fontScale;
+ }
+ if (delta.mcc != 0 && mcc != delta.mcc) {
+ changed |= ActivityInfo.CONFIG_MCC;
+ mcc = delta.mcc;
+ }
+ if (delta.mnc != 0 && mnc != delta.mnc) {
+ changed |= ActivityInfo.CONFIG_MNC;
+ mnc = delta.mnc;
+ }
+ if (delta.locale != null
+ && (locale == null || !locale.equals(delta.locale))) {
+ changed |= ActivityInfo.CONFIG_LOCALE;
+ locale = delta.locale != null
+ ? (Locale) delta.locale.clone() : null;
+ }
+ if (delta.userSetLocale && (!userSetLocale || ((changed & ActivityInfo.CONFIG_LOCALE) != 0)))
+ {
+ userSetLocale = true;
+ changed |= ActivityInfo.CONFIG_LOCALE;
+ }
+ if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
+ && touchscreen != delta.touchscreen) {
+ changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
+ touchscreen = delta.touchscreen;
+ }
+ if (delta.keyboard != KEYBOARD_UNDEFINED
+ && keyboard != delta.keyboard) {
+ changed |= ActivityInfo.CONFIG_KEYBOARD;
+ keyboard = delta.keyboard;
+ }
+ if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
+ && keyboardHidden != delta.keyboardHidden) {
+ changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+ keyboardHidden = delta.keyboardHidden;
+ }
+ if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
+ && hardKeyboardHidden != delta.hardKeyboardHidden) {
+ changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+ hardKeyboardHidden = delta.hardKeyboardHidden;
+ }
+ if (delta.navigation != NAVIGATION_UNDEFINED
+ && navigation != delta.navigation) {
+ changed |= ActivityInfo.CONFIG_NAVIGATION;
+ navigation = delta.navigation;
+ }
+ if (delta.orientation != ORIENTATION_UNDEFINED
+ && orientation != delta.orientation) {
+ changed |= ActivityInfo.CONFIG_ORIENTATION;
+ orientation = delta.orientation;
+ }
+
+ return changed;
+ }
+
+ /**
+ * Return a bit mask of the differences between this Configuration
+ * object and the given one. Does not change the values of either. Any
+ * undefined fields in <var>delta</var> are ignored.
+ * @return Returns a bit mask indicating which configuration
+ * values has changed, containing any combination of
+ * {@link android.content.pm.ActivityInfo#CONFIG_FONT_SCALE
+ * PackageManager.ActivityInfo.CONFIG_FONT_SCALE},
+ * {@link android.content.pm.ActivityInfo#CONFIG_MCC
+ * PackageManager.ActivityInfo.CONFIG_MCC},
+ * {@link android.content.pm.ActivityInfo#CONFIG_MNC
+ * PackageManager.ActivityInfo.CONFIG_MNC},
+ * {@link android.content.pm.ActivityInfo#CONFIG_LOCALE
+ * PackageManager.ActivityInfo.CONFIG_LOCALE},
+ * {@link android.content.pm.ActivityInfo#CONFIG_TOUCHSCREEN
+ * PackageManager.ActivityInfo.CONFIG_TOUCHSCREEN},
+ * {@link android.content.pm.ActivityInfo#CONFIG_KEYBOARD
+ * PackageManager.ActivityInfo.CONFIG_KEYBOARD},
+ * {@link android.content.pm.ActivityInfo#CONFIG_NAVIGATION
+ * PackageManager.ActivityInfo.CONFIG_NAVIGATION}, or
+ * {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
+ * PackageManager.ActivityInfo.CONFIG_ORIENTATION}.
+ */
+ public int diff(Configuration delta) {
+ int changed = 0;
+ if (delta.fontScale > 0 && fontScale != delta.fontScale) {
+ changed |= ActivityInfo.CONFIG_FONT_SCALE;
+ }
+ if (delta.mcc != 0 && mcc != delta.mcc) {
+ changed |= ActivityInfo.CONFIG_MCC;
+ }
+ if (delta.mnc != 0 && mnc != delta.mnc) {
+ changed |= ActivityInfo.CONFIG_MNC;
+ }
+ if (delta.locale != null
+ && (locale == null || !locale.equals(delta.locale))) {
+ changed |= ActivityInfo.CONFIG_LOCALE;
+ }
+ if (delta.touchscreen != TOUCHSCREEN_UNDEFINED
+ && touchscreen != delta.touchscreen) {
+ changed |= ActivityInfo.CONFIG_TOUCHSCREEN;
+ }
+ if (delta.keyboard != KEYBOARD_UNDEFINED
+ && keyboard != delta.keyboard) {
+ changed |= ActivityInfo.CONFIG_KEYBOARD;
+ }
+ if (delta.keyboardHidden != KEYBOARDHIDDEN_UNDEFINED
+ && keyboardHidden != delta.keyboardHidden) {
+ changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+ }
+ if (delta.hardKeyboardHidden != HARDKEYBOARDHIDDEN_UNDEFINED
+ && hardKeyboardHidden != delta.hardKeyboardHidden) {
+ changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
+ }
+ if (delta.navigation != NAVIGATION_UNDEFINED
+ && navigation != delta.navigation) {
+ changed |= ActivityInfo.CONFIG_NAVIGATION;
+ }
+ if (delta.orientation != ORIENTATION_UNDEFINED
+ && orientation != delta.orientation) {
+ changed |= ActivityInfo.CONFIG_ORIENTATION;
+ }
+
+ return changed;
+ }
+
+ /**
+ * Determine if a new resource needs to be loaded from the bit set of
+ * configuration changes returned by {@link #updateFrom(Configuration)}.
+ *
+ * @param configChanges The mask of changes configurations as returned by
+ * {@link #updateFrom(Configuration)}.
+ * @param interestingChanges The configuration changes that the resource
+ * can handled, as given in {@link android.util.TypedValue#changingConfigurations}.
+ *
+ * @return Return true if the resource needs to be loaded, else false.
+ */
+ public static boolean needNewResources(int configChanges, int interestingChanges) {
+ return (configChanges & (interestingChanges|ActivityInfo.CONFIG_FONT_SCALE)) != 0;
+ }
+
+ /**
+ * Parcelable methods
+ */
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeFloat(fontScale);
+ dest.writeInt(mcc);
+ dest.writeInt(mnc);
+ if (locale == null) {
+ dest.writeInt(0);
+ } else {
+ dest.writeInt(1);
+ dest.writeString(locale.getLanguage());
+ dest.writeString(locale.getCountry());
+ dest.writeString(locale.getVariant());
+ }
+ if(userSetLocale) {
+ dest.writeInt(1);
+ } else {
+ dest.writeInt(0);
+ }
+ dest.writeInt(touchscreen);
+ dest.writeInt(keyboard);
+ dest.writeInt(keyboardHidden);
+ dest.writeInt(hardKeyboardHidden);
+ dest.writeInt(navigation);
+ dest.writeInt(orientation);
+ }
+
+ public static final Parcelable.Creator<Configuration> CREATOR
+ = new Parcelable.Creator<Configuration>() {
+ public Configuration createFromParcel(Parcel source) {
+ return new Configuration(source);
+ }
+
+ public Configuration[] newArray(int size) {
+ return new Configuration[size];
+ }
+ };
+
+ /**
+ * Construct this Configuration object, reading from the Parcel.
+ */
+ private Configuration(Parcel source) {
+ fontScale = source.readFloat();
+ mcc = source.readInt();
+ mnc = source.readInt();
+ if (source.readInt() != 0) {
+ locale = new Locale(source.readString(), source.readString(),
+ source.readString());
+ }
+ userSetLocale = (source.readInt()==1);
+ touchscreen = source.readInt();
+ keyboard = source.readInt();
+ keyboardHidden = source.readInt();
+ hardKeyboardHidden = source.readInt();
+ navigation = source.readInt();
+ orientation = source.readInt();
+ }
+
+ public int compareTo(Configuration that) {
+ int n;
+ float a = this.fontScale;
+ float b = that.fontScale;
+ if (a < b) return -1;
+ if (a > b) return 1;
+ n = this.mcc - that.mcc;
+ if (n != 0) return n;
+ n = this.mnc - that.mnc;
+ if (n != 0) return n;
+ n = this.locale.getLanguage().compareTo(that.locale.getLanguage());
+ if (n != 0) return n;
+ n = this.locale.getCountry().compareTo(that.locale.getCountry());
+ if (n != 0) return n;
+ n = this.locale.getVariant().compareTo(that.locale.getVariant());
+ if (n != 0) return n;
+ n = this.touchscreen - that.touchscreen;
+ if (n != 0) return n;
+ n = this.keyboard - that.keyboard;
+ if (n != 0) return n;
+ n = this.keyboardHidden - that.keyboardHidden;
+ if (n != 0) return n;
+ n = this.hardKeyboardHidden - that.hardKeyboardHidden;
+ if (n != 0) return n;
+ n = this.navigation - that.navigation;
+ if (n != 0) return n;
+ n = this.orientation - that.orientation;
+ //if (n != 0) return n;
+ return n;
+ }
+
+ public boolean equals(Configuration that) {
+ if (that == null) return false;
+ if (that == this) return true;
+ return this.compareTo(that) == 0;
+ }
+
+ public boolean equals(Object that) {
+ try {
+ return equals((Configuration)that);
+ } catch (ClassCastException e) {
+ }
+ return false;
+ }
+
+ public int hashCode() {
+ return ((int)this.fontScale) + this.mcc + this.mnc
+ + this.locale.hashCode() + this.touchscreen
+ + this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
+ + this.navigation + this.orientation;
+ }
+}
diff --git a/core/java/android/content/res/PluralRules.java b/core/java/android/content/res/PluralRules.java
new file mode 100644
index 0000000..2dce3c1
--- /dev/null
+++ b/core/java/android/content/res/PluralRules.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2007 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 java.util.Locale;
+
+/*
+ * Yuck-o. This is not the right way to implement this. When the ICU PluralRules
+ * object has been integrated to android, we should switch to that. For now, yuck-o.
+ */
+
+abstract class PluralRules {
+
+ static final int QUANTITY_OTHER = 0x0000;
+ static final int QUANTITY_ZERO = 0x0001;
+ static final int QUANTITY_ONE = 0x0002;
+ static final int QUANTITY_TWO = 0x0004;
+ static final int QUANTITY_FEW = 0x0008;
+ static final int QUANTITY_MANY = 0x0010;
+
+ static final int ID_OTHER = 0x01000004;
+
+ abstract int quantityForNumber(int n);
+
+ final int attrForNumber(int n) {
+ return PluralRules.attrForQuantity(quantityForNumber(n));
+ }
+
+ static final int attrForQuantity(int quantity) {
+ // see include/utils/ResourceTypes.h
+ switch (quantity) {
+ case QUANTITY_ZERO: return 0x01000005;
+ case QUANTITY_ONE: return 0x01000006;
+ case QUANTITY_TWO: return 0x01000007;
+ case QUANTITY_FEW: return 0x01000008;
+ case QUANTITY_MANY: return 0x01000009;
+ default: return ID_OTHER;
+ }
+ }
+
+ static final String stringForQuantity(int quantity) {
+ switch (quantity) {
+ case QUANTITY_ZERO:
+ return "zero";
+ case QUANTITY_ONE:
+ return "one";
+ case QUANTITY_TWO:
+ return "two";
+ case QUANTITY_FEW:
+ return "few";
+ case QUANTITY_MANY:
+ return "many";
+ default:
+ return "other";
+ }
+ }
+
+ static final PluralRules ruleForLocale(Locale locale) {
+ String lang = locale.getLanguage();
+ if ("cs".equals(lang)) {
+ if (cs == null) cs = new cs();
+ return cs;
+ }
+ else {
+ if (en == null) en = new en();
+ return en;
+ }
+ }
+
+ private static PluralRules cs;
+ private static class cs extends PluralRules {
+ int quantityForNumber(int n) {
+ if (n == 1) {
+ return QUANTITY_ONE;
+ }
+ else if (n >= 2 && n <= 4) {
+ return QUANTITY_FEW;
+ }
+ else {
+ return QUANTITY_OTHER;
+ }
+ }
+ }
+
+ private static PluralRules en;
+ private static class en extends PluralRules {
+ int quantityForNumber(int n) {
+ if (n == 1) {
+ return QUANTITY_ONE;
+ }
+ else {
+ return QUANTITY_OTHER;
+ }
+ }
+ }
+}
+
diff --git a/core/java/android/content/res/Resources.java b/core/java/android/content/res/Resources.java
new file mode 100644
index 0000000..1a963f6
--- /dev/null
+++ b/core/java/android/content/res/Resources.java
@@ -0,0 +1,1890 @@
+/*
+ * Copyright (C) 2006 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.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.graphics.Movie;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.TypedValue;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.ref.WeakReference;
+
+/**
+ * Class for accessing an application's resources. This sits on top of the
+ * asset manager of the application (accessible through getAssets()) and
+ * provides a higher-level API for getting typed data from the assets.
+ */
+public class Resources {
+ static final String TAG = "Resources";
+ private static final boolean DEBUG_LOAD = false;
+ private static final boolean DEBUG_CONFIG = false;
+ private static final boolean TRACE_FOR_PRELOAD = false;
+
+ private static final int sSdkVersion = SystemProperties.getInt(
+ "ro.build.version.sdk", 0);
+ private static final Object mSync = new Object();
+ private static Resources mSystem = null;
+
+ // Information about preloaded resources. Note that they are not
+ // protected by a lock, because while preloading in zygote we are all
+ // single-threaded, and after that these are immutable.
+ private static final SparseArray<Drawable.ConstantState> mPreloadedDrawables
+ = new SparseArray<Drawable.ConstantState>();
+ private static final SparseArray<ColorStateList> mPreloadedColorStateLists
+ = new SparseArray<ColorStateList>();
+ private static boolean mPreloaded;
+
+ /*package*/ final TypedValue mTmpValue = new TypedValue();
+
+ // These are protected by the mTmpValue lock.
+ private final SparseArray<WeakReference<Drawable.ConstantState> > mDrawableCache
+ = new SparseArray<WeakReference<Drawable.ConstantState> >();
+ private final SparseArray<WeakReference<ColorStateList> > mColorStateListCache
+ = new SparseArray<WeakReference<ColorStateList> >();
+ private boolean mPreloading;
+
+ /*package*/ TypedArray mCachedStyledAttributes = null;
+
+ private int mLastCachedXmlBlockIndex = -1;
+ private final int[] mCachedXmlBlockIds = { 0, 0, 0, 0 };
+ private final XmlBlock[] mCachedXmlBlocks = new XmlBlock[4];
+
+ /*package*/ final AssetManager mAssets;
+ private final Configuration mConfiguration = new Configuration();
+ /*package*/ final DisplayMetrics mMetrics = new DisplayMetrics();
+ PluralRules mPluralRule;
+
+ /**
+ * This exception is thrown by the resource APIs when a requested resource
+ * can not be found.
+ */
+ public static class NotFoundException extends RuntimeException {
+ public NotFoundException() {
+ }
+
+ public NotFoundException(String name) {
+ super(name);
+ }
+ }
+
+ /**
+ * Create a new Resources object on top of an existing set of assets in an
+ * AssetManager.
+ *
+ * @param assets Previously created AssetManager.
+ * @param metrics Current display metrics to consider when
+ * selecting/computing resource values.
+ * @param config Desired device configuration to consider when
+ * selecting/computing resource values (optional).
+ */
+ public Resources(AssetManager assets, DisplayMetrics metrics,
+ Configuration config) {
+ mAssets = assets;
+ mConfiguration.setToDefaults();
+ mMetrics.setToDefaults();
+ updateConfiguration(config, metrics);
+ assets.ensureStringBlocks();
+ }
+
+ /**
+ * Return a global shared Resources object that provides access to only
+ * system resources (no application resources), and is not configured for
+ * the current screen (can not use dimension units, does not change based
+ * on orientation, etc).
+ */
+ public static Resources getSystem() {
+ synchronized (mSync) {
+ Resources ret = mSystem;
+ if (ret == null) {
+ ret = new Resources();
+ mSystem = ret;
+ }
+
+ return ret;
+ }
+ }
+
+ /**
+ * Return the string value associated with a particular resource ID. The
+ * returned object will be a String if this is a plain string; it will be
+ * some other type of CharSequence if it is styled.
+ * {@more}
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return CharSequence The string data associated with the resource, plus
+ * possibly styled text information.
+ */
+ public CharSequence getText(int id) throws NotFoundException {
+ CharSequence res = mAssets.getResourceText(id);
+ if (res != null) {
+ return res;
+ }
+ throw new NotFoundException("String resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ /**
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return CharSequence The string data associated with the resource, plus
+ * possibly styled text information.
+ */
+ public CharSequence getQuantityText(int id, int quantity) throws NotFoundException {
+ PluralRules rule = getPluralRule();
+ CharSequence res = mAssets.getResourceBagText(id, rule.attrForNumber(quantity));
+ if (res != null) {
+ return res;
+ }
+ res = mAssets.getResourceBagText(id, PluralRules.ID_OTHER);
+ if (res != null) {
+ return res;
+ }
+ throw new NotFoundException("Plural resource ID #0x" + Integer.toHexString(id)
+ + " quantity=" + quantity
+ + " item=" + PluralRules.stringForQuantity(rule.quantityForNumber(quantity)));
+ }
+
+ private PluralRules getPluralRule() {
+ synchronized (mSync) {
+ if (mPluralRule == null) {
+ mPluralRule = PluralRules.ruleForLocale(mConfiguration.locale);
+ }
+ return mPluralRule;
+ }
+ }
+
+ /**
+ * Return the string value associated with a particular resource ID. It
+ * will be stripped of any styled text information.
+ * {@more}
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return String The string data associated with the resource,
+ * stripped of styled text information.
+ */
+ public String getString(int id) throws NotFoundException {
+ CharSequence res = getText(id);
+ if (res != null) {
+ return res.toString();
+ }
+ throw new NotFoundException("String resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+
+ /**
+ * Return the string value associated with a particular resource ID,
+ * substituting the format arguments as defined in {@link java.util.Formatter}
+ * and {@link java.lang.String#format}. It will be stripped of any styled text
+ * information.
+ * {@more}
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @param formatArgs The format arguments that will be used for substitution.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return String The string data associated with the resource,
+ * stripped of styled text information.
+ */
+ public String getString(int id, Object... formatArgs) throws NotFoundException {
+ String raw = getString(id);
+ return String.format(mConfiguration.locale, raw, formatArgs);
+ }
+
+ /**
+ * Return the string value associated with a particular resource ID for a particular
+ * numerical quantity, substituting the format arguments as defined in
+ * {@link java.util.Formatter} and {@link java.lang.String#format}. It will be
+ * stripped of any styled text information.
+ * {@more}
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ * @param quantity The number used to get the correct string for the current language's
+ * plural rules.
+ * @param formatArgs The format arguments that will be used for substitution.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return String The string data associated with the resource,
+ * stripped of styled text information.
+ */
+ public String getQuantityString(int id, int quantity, Object... formatArgs)
+ throws NotFoundException {
+ String raw = getQuantityText(id, quantity).toString();
+ return String.format(mConfiguration.locale, raw, formatArgs);
+ }
+
+ /**
+ * Return the string value associated with a particular resource ID for a particular
+ * numerical quantity.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ * @param quantity The number used to get the correct string for the current language's
+ * plural rules.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return String The string data associated with the resource,
+ * stripped of styled text information.
+ */
+ public String getQuantityString(int id, int quantity) throws NotFoundException {
+ return getQuantityText(id, quantity).toString();
+ }
+
+ /**
+ * Return the string value associated with a particular resource ID. The
+ * returned object will be a String if this is a plain string; it will be
+ * some other type of CharSequence if it is styled.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @param def The default CharSequence to return.
+ *
+ * @return CharSequence The string data associated with the resource, plus
+ * possibly styled text information, or def if id is 0 or not found.
+ */
+ public CharSequence getText(int id, CharSequence def) {
+ CharSequence res = id != 0 ? mAssets.getResourceText(id) : null;
+ return res != null ? res : def;
+ }
+
+ /**
+ * Return the styled text array associated with a particular resource ID.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return The styled text array associated with the resource.
+ */
+ public CharSequence[] getTextArray(int id) throws NotFoundException {
+ CharSequence[] res = mAssets.getResourceTextArray(id);
+ if (res != null) {
+ return res;
+ }
+ throw new NotFoundException("Text array resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ /**
+ * Return the string array associated with a particular resource ID.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return The string array associated with the resource.
+ */
+ public String[] getStringArray(int id) throws NotFoundException {
+ String[] res = mAssets.getResourceStringArray(id);
+ if (res != null) {
+ return res;
+ }
+ throw new NotFoundException("String array resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ /**
+ * Return the int array associated with a particular resource ID.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return The int array associated with the resource.
+ */
+ public int[] getIntArray(int id) throws NotFoundException {
+ int[] res = mAssets.getArrayIntResource(id);
+ if (res != null) {
+ return res;
+ }
+ throw new NotFoundException("Int array resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ /**
+ * Return an array of heterogeneous values.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Returns a TypedArray holding an array of the array values.
+ * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+ * when done with it.
+ */
+ public TypedArray obtainTypedArray(int id) throws NotFoundException {
+ int len = mAssets.getArraySize(id);
+ if (len < 0) {
+ throw new NotFoundException("Array resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ TypedArray array = getCachedStyledAttributes(len);
+ array.mLength = mAssets.retrieveArray(id, array.mData);
+ array.mIndices[0] = 0;
+
+ return array;
+ }
+
+ /**
+ * Retrieve a dimensional for a particular resource ID. Unit
+ * conversions are based on the current {@link DisplayMetrics} associated
+ * with the resources.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @return Resource dimension value multiplied by the appropriate
+ * metric.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @see #getDimensionPixelOffset
+ * @see #getDimensionPixelSize
+ */
+ public float getDimension(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimension(value.data, mMetrics);
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
+ * Retrieve a dimensional for a particular resource ID 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 id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @return Resource dimension value multiplied by the appropriate
+ * metric and truncated to integer pixels.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @see #getDimension
+ * @see #getDimensionPixelSize
+ */
+ public int getDimensionPixelOffset(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelOffset(
+ value.data, mMetrics);
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
+ * Retrieve a dimensional for a particular resource ID 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 id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @return Resource dimension value multiplied by the appropriate
+ * metric and truncated to integer pixels.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @see #getDimension
+ * @see #getDimensionPixelOffset
+ */
+ public int getDimensionPixelSize(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelSize(
+ value.data, mMetrics);
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
+ * Retrieve a fractional unit for a particular resource ID.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ * @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.
+ *
+ * @return Attribute fractional value multiplied by the appropriate
+ * base value.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ */
+ public float getFraction(int id, int base, int pbase) {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type == TypedValue.TYPE_FRACTION) {
+ return TypedValue.complexToFraction(value.data, base, pbase);
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
+ * Return a drawable object associated with a particular resource ID.
+ * Various types of objects will be returned depending on the underlying
+ * resource -- for example, a solid color, PNG image, scalable image, etc.
+ * The Drawable API hides these implementation details.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Drawable An object that can be used to draw this resource.
+ */
+ public Drawable getDrawable(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ return loadDrawable(value, id);
+ }
+ }
+
+ /**
+ * Return a movie object associated with the particular resource ID.
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ */
+ public Movie getMovie(int id) throws NotFoundException {
+ InputStream is = openRawResource(id);
+ Movie movie = Movie.decodeStream(is);
+ try {
+ is.close();
+ }
+ catch (java.io.IOException e) {
+ // don't care, since the return value is valid
+ }
+ return movie;
+ }
+
+ /**
+ * Return a color integer associated with a particular resource ID.
+ * If the resource holds a complex
+ * {@link android.content.res.ColorStateList}, then the default color from
+ * the set is returned.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Returns a single color value in the form 0xAARRGGBB.
+ */
+ public int getColor(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type >= TypedValue.TYPE_FIRST_INT
+ && value.type <= TypedValue.TYPE_LAST_INT) {
+ return value.data;
+ } else if (value.type == TypedValue.TYPE_STRING) {
+ ColorStateList csl = loadColorStateList(mTmpValue, id);
+ return csl.getDefaultColor();
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
+ * Return a color state list associated with a particular resource ID. The
+ * resource may contain either a single raw color value, or a complex
+ * {@link android.content.res.ColorStateList} holding multiple possible colors.
+ *
+ * @param id The desired resource identifier of a {@link ColorStateList},
+ * as generated by the aapt tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Returns a ColorStateList object containing either a single
+ * solid color or multiple colors that can be selected based on a state.
+ */
+ public ColorStateList getColorStateList(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ return loadColorStateList(value, id);
+ }
+ }
+
+ /**
+ * Return a boolean associated with a particular resource ID. This can be
+ * used with any integral resource value, and will return true if it is
+ * non-zero.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Returns the boolean value contained in the resource.
+ */
+ public boolean getBoolean(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type >= TypedValue.TYPE_FIRST_INT
+ && value.type <= TypedValue.TYPE_LAST_INT) {
+ return value.data != 0;
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
+ * Return an integer associated with a particular resource ID.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Returns the integer value contained in the resource.
+ */
+ public int getInteger(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type >= TypedValue.TYPE_FIRST_INT
+ && value.type <= TypedValue.TYPE_LAST_INT) {
+ return value.data;
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /**
+ * Return an XmlResourceParser through which you can read a view layout
+ * description for the given resource ID. This parser has limited
+ * functionality -- in particular, you can't change its input, and only
+ * the high-level events are available.
+ *
+ * <p>This function is really a simple wrapper for calling
+ * {@link #getXml} with a layout resource.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return A new parser object through which you can read
+ * the XML data.
+ *
+ * @see #getXml
+ */
+ public XmlResourceParser getLayout(int id) throws NotFoundException {
+ return loadXmlResourceParser(id, "layout");
+ }
+
+ /**
+ * Return an XmlResourceParser through which you can read an animation
+ * description for the given resource ID. This parser has limited
+ * functionality -- in particular, you can't change its input, and only
+ * the high-level events are available.
+ *
+ * <p>This function is really a simple wrapper for calling
+ * {@link #getXml} with an animation resource.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return A new parser object through which you can read
+ * the XML data.
+ *
+ * @see #getXml
+ */
+ public XmlResourceParser getAnimation(int id) throws NotFoundException {
+ return loadXmlResourceParser(id, "anim");
+ }
+
+ /**
+ * Return an XmlResourceParser through which you can read a generic XML
+ * resource for the given resource ID.
+ *
+ * <p>The XmlPullParser implementation returned here has some limited
+ * functionality. In particular, you can't change its input, and only
+ * high-level parsing events are available (since the document was
+ * pre-parsed for you at build time, which involved merging text and
+ * stripping comments).
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return A new parser object through which you can read
+ * the XML data.
+ *
+ * @see android.util.AttributeSet
+ */
+ public XmlResourceParser getXml(int id) throws NotFoundException {
+ return loadXmlResourceParser(id, "xml");
+ }
+
+ /**
+ * Open a data stream for reading a raw resource. This can only be used
+ * with resources whose value is the name of an asset files -- that is, it can be
+ * used to open drawable, sound, and raw resources; it will fail on string
+ * and color resources.
+ *
+ * @param id The resource identifier to open, as generated by the appt
+ * tool.
+ *
+ * @return InputStream Access to the resource data.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ */
+ public InputStream openRawResource(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ return openRawResource(id, mTmpValue);
+ }
+ }
+
+ /**
+ * Open a data stream for reading a raw resource. This can only be used
+ * with resources whose value is the name of an asset files -- that is, it can be
+ * used to open drawable, sound, and raw resources; it will fail on string
+ * and color resources.
+ *
+ * @param id The resource identifier to open, as generated by the appt tool.
+ * @param value The TypedValue object to hold the resource information.
+ *
+ * @return InputStream Access to the resource data.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @hide Pending API council approval
+ */
+ public InputStream openRawResource(int id, TypedValue value) throws NotFoundException {
+ getValue(id, value, true);
+
+ try {
+ return mAssets.openNonAsset(value.assetCookie, value.string.toString(),
+ AssetManager.ACCESS_STREAMING);
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException("File " + value.string.toString() +
+ " from drawable resource ID #0x" + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ }
+
+ /**
+ * Open a file descriptor for reading a raw resource. This can only be used
+ * with resources whose value is the name of an asset files -- that is, it can be
+ * used to open drawable, sound, and raw resources; it will fail on string
+ * and color resources.
+ *
+ * <p>This function only works for resources that are stored in the package
+ * as uncompressed data, which typically includes things like mp3 files
+ * and png images.
+ *
+ * @param id The resource identifier to open, as generated by the appt
+ * tool.
+ *
+ * @return AssetFileDescriptor A new file descriptor you can use to read
+ * the resource. This includes the file descriptor itself, as well as the
+ * offset and length of data where the resource appears in the file. A
+ * null is returned if the file exists but is compressed.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ */
+ public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+
+ try {
+ return mAssets.openNonAssetFd(
+ value.assetCookie, value.string.toString());
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException(
+ "File " + value.string.toString()
+ + " from drawable resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+
+ }
+ }
+
+ /**
+ * Return the raw data associated with a particular resource ID.
+ *
+ * @param id The desired resource identifier, as generated by the aapt
+ * tool. This integer encodes the package, type, and resource
+ * entry. The value 0 is an invalid identifier.
+ * @param outValue Object in which to place the resource data.
+ * @param resolveRefs If true, a resource that is a reference to another
+ * resource will be followed so that you receive the
+ * actual final resource data. If false, the TypedValue
+ * will be filled in with the reference itself.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ */
+ public void getValue(int id, TypedValue outValue, boolean resolveRefs)
+ throws NotFoundException {
+ boolean found = mAssets.getResourceValue(id, outValue, resolveRefs);
+ if (found) {
+ return;
+ }
+ throw new NotFoundException("Resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ /**
+ * Return the raw data associated with a particular resource ID.
+ * See getIdentifier() for information on how names are mapped to resource
+ * IDs, and getString(int) for information on how string resources are
+ * retrieved.
+ *
+ * <p>Note: use of this function is discouraged. It is much more
+ * efficient to retrieve resources by identifier than by name.
+ *
+ * @param name The name of the desired resource. This is passed to
+ * getIdentifier() with a default type of "string".
+ * @param outValue Object in which to place the resource data.
+ * @param resolveRefs If true, a resource that is a reference to another
+ * resource will be followed so that you receive the
+ * actual final resource data. If false, the TypedValue
+ * will be filled in with the reference itself.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ */
+ public void getValue(String name, TypedValue outValue, boolean resolveRefs)
+ throws NotFoundException {
+ int id = getIdentifier(name, "string", null);
+ if (id != 0) {
+ getValue(id, outValue, resolveRefs);
+ return;
+ }
+ throw new NotFoundException("String resource name " + name);
+ }
+
+ /**
+ * This class holds the current attribute values for a particular theme.
+ * In other words, a Theme is a set of values for resource attributes;
+ * these are used in conjunction with {@link TypedArray}
+ * to resolve the final value for an attribute.
+ *
+ * <p>The Theme's attributes come into play in two ways: (1) a styled
+ * attribute can explicit reference a value in the theme through the
+ * "?themeAttribute" syntax; (2) if no value has been defined for a
+ * particular styled attribute, as a last resort we will try to find that
+ * attribute's value in the Theme.
+ *
+ * <p>You will normally use the {@link #obtainStyledAttributes} APIs to
+ * retrieve XML attributes with style and theme information applied.
+ */
+ public final class Theme {
+ /**
+ * Place new attribute values into the theme. The style resource
+ * specified by <var>resid</var> will be retrieved from this Theme's
+ * resources, its values placed into the Theme object.
+ *
+ * <p>The semantics of this function depends on the <var>force</var>
+ * argument: If false, only values that are not already defined in
+ * the theme will be copied from the system resource; otherwise, if
+ * any of the style's attributes are already defined in the theme, the
+ * current values in the theme will be overwritten.
+ *
+ * @param resid The resource ID of a style resource from which to
+ * obtain attribute values.
+ * @param force If true, values in the style resource will always be
+ * used in the theme; otherwise, they will only be used
+ * if not already defined in the theme.
+ */
+ public void applyStyle(int resid, boolean force) {
+ AssetManager.applyThemeStyle(mTheme, resid, force);
+ }
+
+ /**
+ * Set this theme to hold the same contents as the theme
+ * <var>other</var>. If both of these themes are from the same
+ * Resources object, they will be identical after this function
+ * returns. If they are from different Resources, only the resources
+ * they have in common will be set in this theme.
+ *
+ * @param other The existing Theme to copy from.
+ */
+ public void setTo(Theme other) {
+ AssetManager.copyTheme(mTheme, other.mTheme);
+ }
+
+ /**
+ * Return a StyledAttributes holding the values defined by
+ * <var>Theme</var> which are listed in <var>attrs</var>.
+ *
+ * <p>Be sure to call StyledAttributes.recycle() when you are done with
+ * the array.
+ *
+ * @param attrs The desired attributes.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Returns a TypedArray holding an array of the attribute values.
+ * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+ * when done with it.
+ *
+ * @see Resources#obtainAttributes
+ * @see #obtainStyledAttributes(int, int[])
+ * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
+ */
+ public TypedArray obtainStyledAttributes(int[] attrs) {
+ int len = attrs.length;
+ TypedArray array = getCachedStyledAttributes(len);
+ array.mRsrcs = attrs;
+ AssetManager.applyStyle(mTheme, 0, 0, 0, attrs,
+ array.mData, array.mIndices);
+ return array;
+ }
+
+ /**
+ * Return a StyledAttributes holding the values defined by the style
+ * resource <var>resid</var> which are listed in <var>attrs</var>.
+ *
+ * <p>Be sure to call StyledAttributes.recycle() when you are done with
+ * the array.
+ *
+ * @param resid The desired style resource.
+ * @param attrs The desired attributes in the style.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @return Returns a TypedArray holding an array of the attribute values.
+ * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+ * when done with it.
+ *
+ * @see Resources#obtainAttributes
+ * @see #obtainStyledAttributes(int[])
+ * @see #obtainStyledAttributes(AttributeSet, int[], int, int)
+ */
+ public TypedArray obtainStyledAttributes(int resid, int[] attrs)
+ throws NotFoundException {
+ int len = attrs.length;
+ TypedArray array = getCachedStyledAttributes(len);
+ array.mRsrcs = attrs;
+
+ AssetManager.applyStyle(mTheme, 0, resid, 0, attrs,
+ array.mData, array.mIndices);
+ if (false) {
+ int[] data = array.mData;
+
+ System.out.println("**********************************************************");
+ System.out.println("**********************************************************");
+ System.out.println("**********************************************************");
+ System.out.println("Attributes:");
+ String s = " Attrs:";
+ int i;
+ for (i=0; i<attrs.length; i++) {
+ s = s + " 0x" + Integer.toHexString(attrs[i]);
+ }
+ System.out.println(s);
+ s = " Found:";
+ TypedValue value = new TypedValue();
+ for (i=0; i<attrs.length; i++) {
+ int d = i*AssetManager.STYLE_NUM_ENTRIES;
+ value.type = data[d+AssetManager.STYLE_TYPE];
+ value.data = data[d+AssetManager.STYLE_DATA];
+ value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
+ value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
+ s = s + " 0x" + Integer.toHexString(attrs[i])
+ + "=" + value;
+ }
+ System.out.println(s);
+ }
+ return array;
+ }
+
+ /**
+ * Return a StyledAttributes holding the attribute values in
+ * <var>set</var>
+ * that are listed in <var>attrs</var>. In addition, if the given
+ * AttributeSet specifies a style class (through the "style" attribute),
+ * that style will be applied on top of the base attributes it defines.
+ *
+ * <p>Be sure to call StyledAttributes.recycle() when you are done with
+ * the array.
+ *
+ * <p>When determining the final value of a particular attribute, there
+ * are four inputs that come into play:</p>
+ *
+ * <ol>
+ * <li> Any attribute values in the given AttributeSet.
+ * <li> The style resource specified in the AttributeSet (named
+ * "style").
+ * <li> The default style specified by <var>defStyleAttr</var> and
+ * <var>defStyleRes</var>
+ * <li> The base values in this theme.
+ * </ol>
+ *
+ * <p>Each of these inputs is considered in-order, with the first listed
+ * taking precedence over the following ones. In other words, if in the
+ * AttributeSet you have supplied <code><Button
+ * textColor="#ff000000"></code>, then the button's text will
+ * <em>always</em> be black, regardless of what is specified in any of
+ * the styles.
+ *
+ * @param set The base set of attribute values. May be null.
+ * @param attrs The desired attributes to be retrieved.
+ * @param defStyleAttr An attribute in the current theme that contains a
+ * reference to a style resource that supplies
+ * defaults values for the StyledAttributes. Can be
+ * 0 to not look for defaults.
+ * @param defStyleRes A resource identifier of a style resource that
+ * supplies default values for the StyledAttributes,
+ * used only if defStyleAttr is 0 or can not be found
+ * in the theme. Can be 0 to not look for defaults.
+ *
+ * @return Returns a TypedArray holding an array of the attribute values.
+ * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+ * when done with it.
+ *
+ * @see Resources#obtainAttributes
+ * @see #obtainStyledAttributes(int[])
+ * @see #obtainStyledAttributes(int, int[])
+ */
+ public TypedArray obtainStyledAttributes(AttributeSet set,
+ int[] attrs, int defStyleAttr, int defStyleRes) {
+ int len = attrs.length;
+ TypedArray array = getCachedStyledAttributes(len);
+
+ // XXX note that for now we only work with compiled XML files.
+ // To support generic XML files we will need to manually parse
+ // out the attributes from the XML file (applying type information
+ // contained in the resources and such).
+ XmlBlock.Parser parser = (XmlBlock.Parser)set;
+ AssetManager.applyStyle(
+ mTheme, defStyleAttr, defStyleRes,
+ parser != null ? parser.mParseState : 0, attrs,
+ array.mData, array.mIndices);
+
+ array.mRsrcs = attrs;
+ array.mXml = parser;
+
+ if (false) {
+ int[] data = array.mData;
+
+ System.out.println("Attributes:");
+ String s = " Attrs:";
+ int i;
+ for (i=0; i<set.getAttributeCount(); i++) {
+ s = s + " " + set.getAttributeName(i);
+ int id = set.getAttributeNameResource(i);
+ if (id != 0) {
+ s = s + "(0x" + Integer.toHexString(id) + ")";
+ }
+ s = s + "=" + set.getAttributeValue(i);
+ }
+ System.out.println(s);
+ s = " Found:";
+ TypedValue value = new TypedValue();
+ for (i=0; i<attrs.length; i++) {
+ int d = i*AssetManager.STYLE_NUM_ENTRIES;
+ value.type = data[d+AssetManager.STYLE_TYPE];
+ value.data = data[d+AssetManager.STYLE_DATA];
+ value.assetCookie = data[d+AssetManager.STYLE_ASSET_COOKIE];
+ value.resourceId = data[d+AssetManager.STYLE_RESOURCE_ID];
+ s = s + " 0x" + Integer.toHexString(attrs[i])
+ + "=" + value;
+ }
+ System.out.println(s);
+ }
+
+ return array;
+ }
+
+ /**
+ * Retrieve the value of an attribute in the Theme. The contents of
+ * <var>outValue</var> are ultimately filled in by
+ * {@link Resources#getValue}.
+ *
+ * @param resid The resource identifier of the desired theme
+ * attribute.
+ * @param outValue Filled in with the ultimate resource value supplied
+ * by the attribute.
+ * @param resolveRefs If true, resource references will be walked; if
+ * false, <var>outValue</var> may be a
+ * TYPE_REFERENCE. In either case, it will never
+ * be a TYPE_ATTRIBUTE.
+ *
+ * @return boolean Returns true if the attribute was found and
+ * <var>outValue</var> is valid, else false.
+ */
+ public boolean resolveAttribute(int resid, TypedValue outValue,
+ boolean resolveRefs) {
+ boolean got = mAssets.getThemeValue(mTheme, resid, outValue, resolveRefs);
+ if (false) {
+ System.out.println(
+ "resolveAttribute #" + Integer.toHexString(resid)
+ + " got=" + got + ", type=0x" + Integer.toHexString(outValue.type)
+ + ", data=0x" + Integer.toHexString(outValue.data));
+ }
+ return got;
+ }
+
+ /**
+ * Print contents of this theme out to the log. For debugging only.
+ *
+ * @param priority The log priority to use.
+ * @param tag The log tag to use.
+ * @param prefix Text to prefix each line printed.
+ */
+ public void dump(int priority, String tag, String prefix) {
+ AssetManager.dumpTheme(mTheme, priority, tag, prefix);
+ }
+
+ protected void finalize() throws Throwable {
+ super.finalize();
+ mAssets.releaseTheme(mTheme);
+ }
+
+ /*package*/ Theme() {
+ mAssets = Resources.this.mAssets;
+ mTheme = mAssets.createTheme();
+ }
+
+ private final AssetManager mAssets;
+ private final int mTheme;
+ }
+
+ /**
+ * Generate a new Theme object for this set of Resources. It initially
+ * starts out empty.
+ *
+ * @return Theme The newly created Theme container.
+ */
+ public final Theme newTheme() {
+ return new Theme();
+ }
+
+ /**
+ * Retrieve a set of basic attribute values from an AttributeSet, not
+ * performing styling of them using a theme and/or style resources.
+ *
+ * @param set The current attribute values to retrieve.
+ * @param attrs The specific attributes to be retrieved.
+ * @return Returns a TypedArray holding an array of the attribute values.
+ * Be sure to call {@link TypedArray#recycle() TypedArray.recycle()}
+ * when done with it.
+ *
+ * @see Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
+ */
+ public TypedArray obtainAttributes(AttributeSet set, int[] attrs) {
+ int len = attrs.length;
+ TypedArray array = getCachedStyledAttributes(len);
+
+ // XXX note that for now we only work with compiled XML files.
+ // To support generic XML files we will need to manually parse
+ // out the attributes from the XML file (applying type information
+ // contained in the resources and such).
+ XmlBlock.Parser parser = (XmlBlock.Parser)set;
+ mAssets.retrieveAttributes(parser.mParseState, attrs,
+ array.mData, array.mIndices);
+
+ array.mRsrcs = attrs;
+ array.mXml = parser;
+
+ return array;
+ }
+
+ /**
+ * Store the newly updated configuration.
+ */
+ public void updateConfiguration(Configuration config,
+ DisplayMetrics metrics) {
+ synchronized (mTmpValue) {
+ int configChanges = 0xfffffff;
+ if (config != null) {
+ configChanges = mConfiguration.updateFrom(config);
+ }
+ if (metrics != null) {
+ mMetrics.setTo(metrics);
+ }
+ mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
+ String locale = null;
+ if (mConfiguration.locale != null) {
+ locale = mConfiguration.locale.getLanguage();
+ if (mConfiguration.locale.getCountry() != null) {
+ locale += "-" + mConfiguration.locale.getCountry();
+ }
+ }
+ int width, height;
+ if (mMetrics.widthPixels >= mMetrics.heightPixels) {
+ width = mMetrics.widthPixels;
+ height = mMetrics.heightPixels;
+ } else {
+ //noinspection SuspiciousNameCombination
+ width = mMetrics.heightPixels;
+ //noinspection SuspiciousNameCombination
+ height = mMetrics.widthPixels;
+ }
+ int keyboardHidden = mConfiguration.keyboardHidden;
+ if (keyboardHidden == Configuration.HARDKEYBOARDHIDDEN_NO
+ && mConfiguration.hardKeyboardHidden
+ == Configuration.HARDKEYBOARDHIDDEN_YES) {
+ keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
+ }
+ mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
+ locale, mConfiguration.orientation,
+ mConfiguration.touchscreen,
+ (int)(mMetrics.density*160), mConfiguration.keyboard,
+ keyboardHidden, mConfiguration.navigation, width, height,
+ sSdkVersion);
+ int N = mDrawableCache.size();
+ if (DEBUG_CONFIG) {
+ Log.d(TAG, "Cleaning up drawables config changes: 0x"
+ + Integer.toHexString(configChanges));
+ }
+ for (int i=0; i<N; i++) {
+ WeakReference<Drawable.ConstantState> ref = mDrawableCache.valueAt(i);
+ if (ref != null) {
+ Drawable.ConstantState cs = ref.get();
+ if (cs != null) {
+ if (Configuration.needNewResources(
+ configChanges, cs.getChangingConfigurations())) {
+ if (DEBUG_CONFIG) {
+ Log.d(TAG, "FLUSHING #0x"
+ + Integer.toHexString(mDrawableCache.keyAt(i))
+ + " / " + cs + " with changes: 0x"
+ + Integer.toHexString(cs.getChangingConfigurations()));
+ }
+ mDrawableCache.setValueAt(i, null);
+ } else if (DEBUG_CONFIG) {
+ Log.d(TAG, "(Keeping #0x"
+ + Integer.toHexString(mDrawableCache.keyAt(i))
+ + " / " + cs + " with changes: 0x"
+ + Integer.toHexString(cs.getChangingConfigurations())
+ + ")");
+ }
+ }
+ }
+ }
+ mDrawableCache.clear();
+ mColorStateListCache.clear();
+ flushLayoutCache();
+ }
+ synchronized (mSync) {
+ if (mPluralRule != null) {
+ mPluralRule = PluralRules.ruleForLocale(config.locale);
+ }
+ }
+ }
+
+ /**
+ * Update the system resources configuration if they have previously
+ * been initialized.
+ *
+ * @hide
+ */
+ public static void updateSystemConfiguration(Configuration config, DisplayMetrics metrics) {
+ if (mSystem != null) {
+ mSystem.updateConfiguration(config, metrics);
+ //Log.i(TAG, "Updated system resources " + mSystem
+ // + ": " + mSystem.getConfiguration());
+ }
+ }
+
+ /**
+ * Return the current display metrics that are in effect for this resource
+ * object. The returned object should be treated as read-only.
+ *
+ * @return The resource's current display metrics.
+ */
+ public DisplayMetrics getDisplayMetrics() {
+ return mMetrics;
+ }
+
+ /**
+ * Return the current configuration that is in effect for this resource
+ * object. The returned object should be treated as read-only.
+ *
+ * @return The resource's current configuration.
+ */
+ public Configuration getConfiguration() {
+ return mConfiguration;
+ }
+
+ /**
+ * Return a resource identifier for the given resource name. A fully
+ * qualified resource name is of the form "package:type/entry". The first
+ * two components (package and type) are optional if defType and
+ * defPackage, respectively, are specified here.
+ *
+ * <p>Note: use of this function is discouraged. It is much more
+ * efficient to retrieve resources by identifier than by name.
+ *
+ * @param name The name of the desired resource.
+ * @param defType Optional default resource type to find, if "type/" is
+ * not included in the name. Can be null to require an
+ * explicit type.
+ * @param defPackage Optional default package to find, if "package:" is
+ * not included in the name. Can be null to require an
+ * explicit package.
+ *
+ * @return int The associated resource identifier. Returns 0 if no such
+ * resource was found. (0 is not a valid resource ID.)
+ */
+ public int getIdentifier(String name, String defType, String defPackage) {
+ try {
+ return Integer.parseInt(name);
+ } catch (Exception e) {
+ // Ignore
+ }
+ return mAssets.getResourceIdentifier(name, defType, defPackage);
+ }
+
+ /**
+ * Return the full name for a given resource identifier. This name is
+ * a single string of the form "package:type/entry".
+ *
+ * @param resid The resource identifier whose name is to be retrieved.
+ *
+ * @return A string holding the name of the resource.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @see #getResourcePackageName
+ * @see #getResourceTypeName
+ * @see #getResourceEntryName
+ */
+ public String getResourceName(int resid) throws NotFoundException {
+ String str = mAssets.getResourceName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ /**
+ * Return the package name for a given resource identifier.
+ *
+ * @param resid The resource identifier whose package name is to be
+ * retrieved.
+ *
+ * @return A string holding the package name of the resource.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @see #getResourceName
+ */
+ public String getResourcePackageName(int resid) throws NotFoundException {
+ String str = mAssets.getResourcePackageName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ /**
+ * Return the type name for a given resource identifier.
+ *
+ * @param resid The resource identifier whose type name is to be
+ * retrieved.
+ *
+ * @return A string holding the type name of the resource.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @see #getResourceName
+ */
+ public String getResourceTypeName(int resid) throws NotFoundException {
+ String str = mAssets.getResourceTypeName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ /**
+ * Return the entry name for a given resource identifier.
+ *
+ * @param resid The resource identifier whose entry name is to be
+ * retrieved.
+ *
+ * @return A string holding the entry name of the resource.
+ *
+ * @throws NotFoundException Throws NotFoundException if the given ID does not exist.
+ *
+ * @see #getResourceName
+ */
+ public String getResourceEntryName(int resid) throws NotFoundException {
+ String str = mAssets.getResourceEntryName(resid);
+ if (str != null) return str;
+ throw new NotFoundException("Unable to find resource ID #0x"
+ + Integer.toHexString(resid));
+ }
+
+ /**
+ * Parse a series of {@link android.R.styleable#Extra <extra>} tags from
+ * an XML file. You call this when you are at the parent tag of the
+ * extra tags, and it return once all of the child tags have been parsed.
+ * This will call {@link #parseBundleExtra} for each extra tag encountered.
+ *
+ * @param parser The parser from which to retrieve the extras.
+ * @param outBundle A Bundle in which to place all parsed extras.
+ * @throws XmlPullParserException
+ * @throws IOException
+ */
+ public void parseBundleExtras(XmlResourceParser parser, Bundle outBundle)
+ throws XmlPullParserException, IOException {
+ int outerDepth = parser.getDepth();
+ int type;
+ while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+ && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
+ if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
+ continue;
+ }
+
+ String nodeName = parser.getName();
+ if (nodeName.equals("extra")) {
+ parseBundleExtra("extra", parser, outBundle);
+ XmlUtils.skipCurrentTag(parser);
+
+ } else {
+ XmlUtils.skipCurrentTag(parser);
+ }
+ }
+ }
+
+ /**
+ * Parse a name/value pair out of an XML tag holding that data. The
+ * AttributeSet must be holding the data defined by
+ * {@link android.R.styleable#Extra}. The following value types are supported:
+ * <ul>
+ * <li> {@link TypedValue#TYPE_STRING}:
+ * {@link Bundle#putCharSequence Bundle.putCharSequence()}
+ * <li> {@link TypedValue#TYPE_INT_BOOLEAN}:
+ * {@link Bundle#putCharSequence Bundle.putBoolean()}
+ * <li> {@link TypedValue#TYPE_FIRST_INT}-{@link TypedValue#TYPE_LAST_INT}:
+ * {@link Bundle#putCharSequence Bundle.putBoolean()}
+ * <li> {@link TypedValue#TYPE_FLOAT}:
+ * {@link Bundle#putCharSequence Bundle.putFloat()}
+ * </ul>
+ *
+ * @param tagName The name of the tag these attributes come from; this is
+ * only used for reporting error messages.
+ * @param attrs The attributes from which to retrieve the name/value pair.
+ * @param outBundle The Bundle in which to place the parsed value.
+ * @throws XmlPullParserException If the attributes are not valid.
+ */
+ public void parseBundleExtra(String tagName, AttributeSet attrs,
+ Bundle outBundle) throws XmlPullParserException {
+ TypedArray sa = obtainAttributes(attrs,
+ com.android.internal.R.styleable.Extra);
+
+ String name = sa.getString(
+ com.android.internal.R.styleable.Extra_name);
+ if (name == null) {
+ sa.recycle();
+ throw new XmlPullParserException("<" + tagName
+ + "> requires an android:name attribute at "
+ + attrs.getPositionDescription());
+ }
+
+ TypedValue v = sa.peekValue(
+ com.android.internal.R.styleable.Extra_value);
+ if (v != null) {
+ if (v.type == TypedValue.TYPE_STRING) {
+ CharSequence cs = v.coerceToString();
+ outBundle.putCharSequence(name, cs);
+ } else if (v.type == TypedValue.TYPE_INT_BOOLEAN) {
+ outBundle.putBoolean(name, v.data != 0);
+ } else if (v.type >= TypedValue.TYPE_FIRST_INT
+ && v.type <= TypedValue.TYPE_LAST_INT) {
+ outBundle.putInt(name, v.data);
+ } else if (v.type == TypedValue.TYPE_FLOAT) {
+ outBundle.putFloat(name, v.getFloat());
+ } else {
+ sa.recycle();
+ throw new XmlPullParserException("<" + tagName
+ + "> only supports string, integer, float, color, and boolean at "
+ + attrs.getPositionDescription());
+ }
+ } else {
+ sa.recycle();
+ throw new XmlPullParserException("<" + tagName
+ + "> requires an android:value or android:resource attribute at "
+ + attrs.getPositionDescription());
+ }
+
+ sa.recycle();
+ }
+
+ /**
+ * Retrieve underlying AssetManager storage for these resources.
+ */
+ public final AssetManager getAssets() {
+ return mAssets;
+ }
+
+ /**
+ * Call this to remove all cached loaded layout resources from the
+ * Resources object. Only intended for use with performance testing
+ * tools.
+ */
+ public final void flushLayoutCache() {
+ synchronized (mCachedXmlBlockIds) {
+ // First see if this block is in our cache.
+ final int num = mCachedXmlBlockIds.length;
+ for (int i=0; i<num; i++) {
+ mCachedXmlBlockIds[i] = -0;
+ XmlBlock oldBlock = mCachedXmlBlocks[i];
+ if (oldBlock != null) {
+ oldBlock.close();
+ }
+ mCachedXmlBlocks[i] = null;
+ }
+ }
+ }
+
+ /**
+ * Start preloading of resource data using this Resources object. Only
+ * for use by the zygote process for loading common system resources.
+ * {@hide}
+ */
+ public final void startPreloading() {
+ synchronized (mSync) {
+ if (mPreloaded) {
+ throw new IllegalStateException("Resources already preloaded");
+ }
+ mPreloaded = true;
+ mPreloading = true;
+ }
+ }
+
+ /**
+ * Called by zygote when it is done preloading resources, to change back
+ * to normal Resources operation.
+ */
+ public final void finishPreloading() {
+ if (mPreloading) {
+ mPreloading = false;
+ flushLayoutCache();
+ }
+ }
+
+ /*package*/ Drawable loadDrawable(TypedValue value, int id)
+ throws NotFoundException {
+
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) android.util.Log.d("PreloadDrawable", name);
+ }
+ }
+
+ final int key = (value.assetCookie << 24) | value.data;
+ Drawable dr = getCachedDrawable(key);
+
+ if (dr != null) {
+ return dr;
+ }
+
+ Drawable.ConstantState cs = mPreloadedDrawables.get(key);
+ if (cs != null) {
+ dr = cs.newDrawable();
+ } else {
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+ value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ dr = new ColorDrawable(value.data);
+ }
+
+ if (dr == null) {
+ if (value.string == null) {
+ throw new NotFoundException(
+ "Resource is not a Drawable (color or path): " + value);
+ }
+
+ String file = value.string.toString();
+
+ if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
+ + value.assetCookie + ": " + file);
+
+ if (file.endsWith(".xml")) {
+ try {
+ XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "drawable");
+ dr = Drawable.createFromXml(this, rp);
+ rp.close();
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException(
+ "File " + file + " from drawable resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+
+ } else {
+ try {
+ InputStream is = mAssets.openNonAsset(
+ value.assetCookie, file, AssetManager.ACCESS_BUFFER);
+ // System.out.println("Opened file " + file + ": " + is);
+ dr = Drawable.createFromResourceStream(this, value, is, file);
+ is.close();
+ // System.out.println("Created stream: " + dr);
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException(
+ "File " + file + " from drawable resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ }
+ }
+ }
+
+ if (dr != null) {
+ dr.setChangingConfigurations(value.changingConfigurations);
+ cs = dr.getConstantState();
+ if (cs != null) {
+ if (mPreloading) {
+ mPreloadedDrawables.put(key, cs);
+ } else {
+ synchronized (mTmpValue) {
+ //Log.i(TAG, "Saving cached drawable @ #" +
+ // Integer.toHexString(key.intValue())
+ // + " in " + this + ": " + cs);
+ mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
+ }
+ }
+ }
+ }
+
+ return dr;
+ }
+
+ private Drawable getCachedDrawable(int key) {
+ synchronized (mTmpValue) {
+ WeakReference<Drawable.ConstantState> wr = mDrawableCache.get(key);
+ if (wr != null) { // we have the key
+ Drawable.ConstantState entry = wr.get();
+ if (entry != null) {
+ //Log.i(TAG, "Returning cached drawable @ #" +
+ // Integer.toHexString(((Integer)key).intValue())
+ // + " in " + this + ": " + entry);
+ return entry.newDrawable();
+ }
+ else { // our entry has been purged
+ mDrawableCache.delete(key);
+ }
+ }
+ }
+ return null;
+ }
+
+ /*package*/ ColorStateList loadColorStateList(TypedValue value, int id)
+ throws NotFoundException {
+ if (TRACE_FOR_PRELOAD) {
+ // Log only framework resources
+ if ((id >>> 24) == 0x1) {
+ final String name = getResourceName(id);
+ if (name != null) android.util.Log.d("PreloadColorStateList", name);
+ }
+ }
+
+ final int key = (value.assetCookie << 24) | value.data;
+
+ ColorStateList csl;
+
+ if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+ value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+
+ csl = mPreloadedColorStateLists.get(key);
+ if (csl != null) {
+ return csl;
+ }
+
+ csl = ColorStateList.valueOf(value.data);
+ if (mPreloading) {
+ mPreloadedColorStateLists.put(key, csl);
+ }
+
+ return csl;
+ }
+
+ csl = getCachedColorStateList(key);
+ if (csl != null) {
+ return csl;
+ }
+
+ csl = mPreloadedColorStateLists.get(key);
+ if (csl != null) {
+ return csl;
+ }
+
+ if (value.string == null) {
+ throw new NotFoundException(
+ "Resource is not a ColorStateList (color or path): " + value);
+ }
+
+ String file = value.string.toString();
+
+ if (file.endsWith(".xml")) {
+ try {
+ XmlResourceParser rp = loadXmlResourceParser(
+ file, id, value.assetCookie, "colorstatelist");
+ csl = ColorStateList.createFromXml(this, rp);
+ rp.close();
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException(
+ "File " + file + " from color state list resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ } else {
+ throw new NotFoundException(
+ "File " + file + " from drawable resource ID #0x"
+ + Integer.toHexString(id) + ": .xml extension required");
+ }
+
+ if (csl != null) {
+ if (mPreloading) {
+ mPreloadedColorStateLists.put(key, csl);
+ } else {
+ synchronized (mTmpValue) {
+ //Log.i(TAG, "Saving cached color state list @ #" +
+ // Integer.toHexString(key.intValue())
+ // + " in " + this + ": " + csl);
+ mColorStateListCache.put(
+ key, new WeakReference<ColorStateList>(csl));
+ }
+ }
+ }
+
+ return csl;
+ }
+
+ private ColorStateList getCachedColorStateList(int key) {
+ synchronized (mTmpValue) {
+ WeakReference<ColorStateList> wr = mColorStateListCache.get(key);
+ if (wr != null) { // we have the key
+ ColorStateList entry = wr.get();
+ if (entry != null) {
+ //Log.i(TAG, "Returning cached color state list @ #" +
+ // Integer.toHexString(((Integer)key).intValue())
+ // + " in " + this + ": " + entry);
+ return entry;
+ }
+ else { // our entry has been purged
+ mColorStateListCache.delete(key);
+ }
+ }
+ }
+ return null;
+ }
+
+ /*package*/ XmlResourceParser loadXmlResourceParser(int id, String type)
+ throws NotFoundException {
+ synchronized (mTmpValue) {
+ TypedValue value = mTmpValue;
+ getValue(id, value, true);
+ if (value.type == TypedValue.TYPE_STRING) {
+ return loadXmlResourceParser(value.string.toString(), id,
+ value.assetCookie, type);
+ }
+ throw new NotFoundException(
+ "Resource ID #0x" + Integer.toHexString(id) + " type #0x"
+ + Integer.toHexString(value.type) + " is not valid");
+ }
+ }
+
+ /*package*/ XmlResourceParser loadXmlResourceParser(String file, int id,
+ int assetCookie, String type) throws NotFoundException {
+ if (id != 0) {
+ try {
+ // These may be compiled...
+ synchronized (mCachedXmlBlockIds) {
+ // First see if this block is in our cache.
+ final int num = mCachedXmlBlockIds.length;
+ for (int i=0; i<num; i++) {
+ if (mCachedXmlBlockIds[i] == id) {
+ //System.out.println("**** REUSING XML BLOCK! id="
+ // + id + ", index=" + i);
+ return mCachedXmlBlocks[i].newParser();
+ }
+ }
+
+ // Not in the cache, create a new block and put it at
+ // the next slot in the cache.
+ XmlBlock block = mAssets.openXmlBlockAsset(
+ assetCookie, file);
+ if (block != null) {
+ int pos = mLastCachedXmlBlockIndex+1;
+ if (pos >= num) pos = 0;
+ mLastCachedXmlBlockIndex = pos;
+ XmlBlock oldBlock = mCachedXmlBlocks[pos];
+ if (oldBlock != null) {
+ oldBlock.close();
+ }
+ mCachedXmlBlockIds[pos] = id;
+ mCachedXmlBlocks[pos] = block;
+ //System.out.println("**** CACHING NEW XML BLOCK! id="
+ // + id + ", index=" + pos);
+ return block.newParser();
+ }
+ }
+ } catch (Exception e) {
+ NotFoundException rnf = new NotFoundException(
+ "File " + file + " from xml type " + type + " resource ID #0x"
+ + Integer.toHexString(id));
+ rnf.initCause(e);
+ throw rnf;
+ }
+ }
+
+ throw new NotFoundException(
+ "File " + file + " from xml type " + type + " resource ID #0x"
+ + Integer.toHexString(id));
+ }
+
+ private TypedArray getCachedStyledAttributes(int len) {
+ synchronized (mTmpValue) {
+ TypedArray attrs = mCachedStyledAttributes;
+ if (attrs != null) {
+ mCachedStyledAttributes = null;
+
+ attrs.mLength = len;
+ int fullLen = len * AssetManager.STYLE_NUM_ENTRIES;
+ if (attrs.mData.length >= fullLen) {
+ return attrs;
+ }
+ attrs.mData = new int[fullLen];
+ attrs.mIndices = new int[1+len];
+ return attrs;
+ }
+ return new TypedArray(this,
+ new int[len*AssetManager.STYLE_NUM_ENTRIES],
+ new int[1+len], len);
+ }
+ }
+
+ private Resources() {
+ mAssets = AssetManager.getSystem();
+ // NOTE: Intentionally leaving this uninitialized (all values set
+ // to zero), so that anyone who tries to do something that requires
+ // metrics will get a very wrong value.
+ mConfiguration.setToDefaults();
+ mMetrics.setToDefaults();
+ updateConfiguration(null, null);
+ mAssets.ensureStringBlocks();
+ }
+}
+
diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java
new file mode 100644
index 0000000..e684cb8
--- /dev/null
+++ b/core/java/android/content/res/StringBlock.java
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2006 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 android.text.*;
+import android.text.style.*;
+import android.util.Config;
+import android.util.Log;
+import android.util.SparseArray;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Typeface;
+import com.android.internal.util.XmlUtils;
+
+/**
+ * Conveniences for retrieving data out of a compiled string resource.
+ *
+ * {@hide}
+ */
+final class StringBlock {
+ private static final String TAG = "AssetManager";
+ private static final boolean localLOGV = Config.LOGV || false;
+
+ private final int mNative;
+ private final boolean mUseSparse;
+ private final boolean mOwnsNative;
+ private CharSequence[] mStrings;
+ private SparseArray<CharSequence> mSparseStrings;
+ StyleIDs mStyleIDs = null;
+
+ public StringBlock(byte[] data, boolean useSparse) {
+ mNative = nativeCreate(data, 0, data.length);
+ mUseSparse = useSparse;
+ mOwnsNative = true;
+ if (localLOGV) Log.v(TAG, "Created string block " + this
+ + ": " + nativeGetSize(mNative));
+ }
+
+ public StringBlock(byte[] data, int offset, int size, boolean useSparse) {
+ mNative = nativeCreate(data, offset, size);
+ mUseSparse = useSparse;
+ mOwnsNative = true;
+ if (localLOGV) Log.v(TAG, "Created string block " + this
+ + ": " + nativeGetSize(mNative));
+ }
+
+ public CharSequence get(int idx) {
+ synchronized (this) {
+ if (mStrings != null) {
+ CharSequence res = mStrings[idx];
+ if (res != null) {
+ return res;
+ }
+ } else if (mSparseStrings != null) {
+ CharSequence res = mSparseStrings.get(idx);
+ if (res != null) {
+ return res;
+ }
+ } else {
+ final int num = nativeGetSize(mNative);
+ if (mUseSparse && num > 250) {
+ mSparseStrings = new SparseArray<CharSequence>();
+ } else {
+ mStrings = new CharSequence[num];
+ }
+ }
+ String str = nativeGetString(mNative, idx);
+ CharSequence res = str;
+ int[] style = nativeGetStyle(mNative, idx);
+ if (localLOGV) Log.v(TAG, "Got string: " + str);
+ if (localLOGV) Log.v(TAG, "Got styles: " + style);
+ if (style != null) {
+ if (mStyleIDs == null) {
+ mStyleIDs = new StyleIDs();
+ mStyleIDs.boldId = nativeIndexOfString(mNative, "b");
+ mStyleIDs.italicId = nativeIndexOfString(mNative, "i");
+ mStyleIDs.underlineId = nativeIndexOfString(mNative, "u");
+ mStyleIDs.ttId = nativeIndexOfString(mNative, "tt");
+ mStyleIDs.bigId = nativeIndexOfString(mNative, "big");
+ mStyleIDs.smallId = nativeIndexOfString(mNative, "small");
+ mStyleIDs.supId = nativeIndexOfString(mNative, "sup");
+ mStyleIDs.subId = nativeIndexOfString(mNative, "sub");
+ mStyleIDs.strikeId = nativeIndexOfString(mNative, "strike");
+ mStyleIDs.listItemId = nativeIndexOfString(mNative, "li");
+ mStyleIDs.marqueeId = nativeIndexOfString(mNative, "marquee");
+
+ if (localLOGV) Log.v(TAG, "BoldId=" + mStyleIDs.boldId
+ + ", ItalicId=" + mStyleIDs.italicId
+ + ", UnderlineId=" + mStyleIDs.underlineId);
+ }
+
+ res = applyStyles(str, style, mStyleIDs);
+ }
+ if (mStrings != null) mStrings[idx] = res;
+ else mSparseStrings.put(idx, res);
+ return res;
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ if (mOwnsNative) {
+ nativeDestroy(mNative);
+ }
+ }
+
+ static final class StyleIDs {
+ private int boldId;
+ private int italicId;
+ private int underlineId;
+ private int ttId;
+ private int bigId;
+ private int smallId;
+ private int subId;
+ private int supId;
+ private int strikeId;
+ private int listItemId;
+ private int marqueeId;
+ }
+
+ private CharSequence applyStyles(String str, int[] style, StyleIDs ids) {
+ if (style.length == 0)
+ return str;
+
+ SpannableString buffer = new SpannableString(str);
+ int i=0;
+ while (i < style.length) {
+ int type = style[i];
+ if (localLOGV) Log.v(TAG, "Applying style span id=" + type
+ + ", start=" + style[i+1] + ", end=" + style[i+2]);
+
+
+ if (type == ids.boldId) {
+ buffer.setSpan(new StyleSpan(Typeface.BOLD),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.italicId) {
+ buffer.setSpan(new StyleSpan(Typeface.ITALIC),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.underlineId) {
+ buffer.setSpan(new UnderlineSpan(),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.ttId) {
+ buffer.setSpan(new TypefaceSpan("monospace"),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.bigId) {
+ buffer.setSpan(new RelativeSizeSpan(1.25f),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.smallId) {
+ buffer.setSpan(new RelativeSizeSpan(0.8f),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.subId) {
+ buffer.setSpan(new SubscriptSpan(),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.supId) {
+ buffer.setSpan(new SuperscriptSpan(),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.strikeId) {
+ buffer.setSpan(new StrikethroughSpan(),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ } else if (type == ids.listItemId) {
+ addParagraphSpan(buffer, new BulletSpan(10),
+ style[i+1], style[i+2]+1);
+ } else if (type == ids.marqueeId) {
+ buffer.setSpan(TextUtils.TruncateAt.MARQUEE,
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_INCLUSIVE_INCLUSIVE);
+ } else {
+ String tag = nativeGetString(mNative, type);
+
+ if (tag.startsWith("font;")) {
+ String sub;
+
+ sub = subtag(tag, ";height=");
+ if (sub != null) {
+ int size = Integer.parseInt(sub);
+ addParagraphSpan(buffer, new Height(size),
+ style[i+1], style[i+2]+1);
+ }
+
+ sub = subtag(tag, ";size=");
+ if (sub != null) {
+ int size = Integer.parseInt(sub);
+ buffer.setSpan(new AbsoluteSizeSpan(size),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ sub = subtag(tag, ";fgcolor=");
+ if (sub != null) {
+ int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
+ buffer.setSpan(new ForegroundColorSpan(color),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ sub = subtag(tag, ";bgcolor=");
+ if (sub != null) {
+ int color = XmlUtils.convertValueToUnsignedInt(sub, -1);
+ buffer.setSpan(new BackgroundColorSpan(color),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ } else if (tag.startsWith("a;")) {
+ String sub;
+
+ sub = subtag(tag, ";href=");
+ if (sub != null) {
+ buffer.setSpan(new URLSpan(sub),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ } else if (tag.startsWith("annotation;")) {
+ int len = tag.length();
+ int next;
+
+ for (int t = tag.indexOf(';'); t < len; t = next) {
+ int eq = tag.indexOf('=', t);
+ if (eq < 0) {
+ break;
+ }
+
+ next = tag.indexOf(';', eq);
+ if (next < 0) {
+ next = len;
+ }
+
+ String key = tag.substring(t + 1, eq);
+ String value = tag.substring(eq + 1, next);
+
+ buffer.setSpan(new Annotation(key, value),
+ style[i+1], style[i+2]+1,
+ Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+ }
+
+ i += 3;
+ }
+ return new SpannedString(buffer);
+ }
+
+ /**
+ * If a translator has messed up the edges of paragraph-level markup,
+ * fix it to actually cover the entire paragraph that it is attached to
+ * instead of just whatever range they put it on.
+ */
+ private static void addParagraphSpan(Spannable buffer, Object what,
+ int start, int end) {
+ int len = buffer.length();
+
+ if (start != 0 && start != len && buffer.charAt(start - 1) != '\n') {
+ for (start--; start > 0; start--) {
+ if (buffer.charAt(start - 1) == '\n') {
+ break;
+ }
+ }
+ }
+
+ if (end != 0 && end != len && buffer.charAt(end - 1) != '\n') {
+ for (end++; end < len; end++) {
+ if (buffer.charAt(end - 1) == '\n') {
+ break;
+ }
+ }
+ }
+
+ buffer.setSpan(what, start, end, Spannable.SPAN_PARAGRAPH);
+ }
+
+ private static String subtag(String full, String attribute) {
+ int start = full.indexOf(attribute);
+ if (start < 0) {
+ return null;
+ }
+
+ start += attribute.length();
+ int end = full.indexOf(';', start);
+
+ if (end < 0) {
+ return full.substring(start);
+ } else {
+ return full.substring(start, end);
+ }
+ }
+
+ /**
+ * Forces the text line to be the specified height, shrinking/stretching
+ * the ascent if possible, or the descent if shrinking the ascent further
+ * will make the text unreadable.
+ */
+ private static class Height implements LineHeightSpan {
+ private int mSize;
+ private static float sProportion = 0;
+
+ public Height(int size) {
+ mSize = size;
+ }
+
+ public void chooseHeight(CharSequence text, int start, int end,
+ int spanstartv, int v,
+ Paint.FontMetricsInt fm) {
+ if (fm.bottom - fm.top < mSize) {
+ fm.top = fm.bottom - mSize;
+ fm.ascent = fm.ascent - mSize;
+ } else {
+ if (sProportion == 0) {
+ /*
+ * Calculate what fraction of the nominal ascent
+ * the height of a capital letter actually is,
+ * so that we won't reduce the ascent to less than
+ * that unless we absolutely have to.
+ */
+
+ Paint p = new Paint();
+ p.setTextSize(100);
+ Rect r = new Rect();
+ p.getTextBounds("ABCDEFG", 0, 7, r);
+
+ sProportion = (r.top) / p.ascent();
+ }
+
+ int need = (int) Math.ceil(-fm.top * sProportion);
+
+ if (mSize - fm.descent >= need) {
+ /*
+ * It is safe to shrink the ascent this much.
+ */
+
+ fm.top = fm.bottom - mSize;
+ fm.ascent = fm.descent - mSize;
+ } else if (mSize >= need) {
+ /*
+ * We can't show all the descent, but we can at least
+ * show all the ascent.
+ */
+
+ fm.top = fm.ascent = -need;
+ fm.bottom = fm.descent = fm.top + mSize;
+ } else {
+ /*
+ * Show as much of the ascent as we can, and no descent.
+ */
+
+ fm.top = fm.ascent = -mSize;
+ fm.bottom = fm.descent = 0;
+ }
+ }
+ }
+ }
+
+ /**
+ * Create from an existing string block native object. This is
+ * -extremely- dangerous -- only use it if you absolutely know what you
+ * are doing! The given native object must exist for the entire lifetime
+ * of this newly creating StringBlock.
+ */
+ StringBlock(int obj, boolean useSparse) {
+ mNative = obj;
+ mUseSparse = useSparse;
+ mOwnsNative = false;
+ if (localLOGV) Log.v(TAG, "Created string block " + this
+ + ": " + nativeGetSize(mNative));
+ }
+
+ private static final native int nativeCreate(byte[] data,
+ int offset,
+ int size);
+ private static final native int nativeGetSize(int obj);
+ private static final native String nativeGetString(int obj, int idx);
+ private static final native int[] nativeGetStyle(int obj, int idx);
+ private static final native int nativeIndexOfString(int obj, String str);
+ private static final native void nativeDestroy(int obj);
+}
diff --git a/core/java/android/content/res/TypedArray.java b/core/java/android/content/res/TypedArray.java
new file mode 100644
index 0000000..3a32c03
--- /dev/null
+++ b/core/java/android/content/res/TypedArray.java
@@ -0,0 +1,688 @@
+package android.content.res;
+
+import android.graphics.drawable.Drawable;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.TypedValue;
+import com.android.internal.util.XmlUtils;
+
+import java.util.Arrays;
+
+/**
+ * Container for an array of values that were retrieved with
+ * {@link Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)}
+ * or {@link Resources#obtainAttributes}. Be
+ * sure to call {@link #recycle} when done with them.
+ *
+ * The indices used to retrieve values from this structure correspond to
+ * the positions of the attributes given to obtainStyledAttributes.
+ */
+public class TypedArray {
+ private final Resources mResources;
+ /*package*/ XmlBlock.Parser mXml;
+ /*package*/ int[] mRsrcs;
+ /*package*/ int[] mData;
+ /*package*/ int[] mIndices;
+ /*package*/ int mLength;
+ private TypedValue mValue = new TypedValue();
+
+ /**
+ * Return the number of values in this array.
+ */
+ public int length() {
+ return mLength;
+ }
+
+ /**
+ * Return the number of indices in the array that actually have data.
+ */
+ public int getIndexCount() {
+ return mIndices[0];
+ }
+
+ /**
+ * Return an index in the array that has data.
+ *
+ * @param at The index you would like to returned, ranging from 0 to
+ * {@link #getIndexCount()}.
+ *
+ * @return The index at the given offset, which can be used with
+ * {@link #getValue} and related APIs.
+ */
+ public int getIndex(int at) {
+ return mIndices[1+at];
+ }
+
+ /**
+ * Return the Resources object this array was loaded from.
+ */
+ public Resources getResources() {
+ return mResources;
+ }
+
+ /**
+ * 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.
+ */
+ public CharSequence getText(int index) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return null;
+ } else if (type == TypedValue.TYPE_STRING) {
+ return loadStringValueAt(index);
+ }
+
+ TypedValue v = mValue;
+ if (getValueAt(index, v)) {
+ Log.w(Resources.TAG, "Converting to string: " + v);
+ return v.coerceToString();
+ }
+ Log.w(Resources.TAG, "getString of bad type: 0x"
+ + Integer.toHexString(type));
+ 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.
+ */
+ public String getString(int index) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return null;
+ } else if (type == TypedValue.TYPE_STRING) {
+ return loadStringValueAt(index).toString();
+ }
+
+ TypedValue v = mValue;
+ if (getValueAt(index, v)) {
+ Log.w(Resources.TAG, "Converting to string: " + v);
+ CharSequence cs = v.coerceToString();
+ return cs != null ? cs.toString() : null;
+ }
+ Log.w(Resources.TAG, "getString of bad type: 0x"
+ + Integer.toHexString(type));
+ return null;
+ }
+
+ /**
+ * Retrieve the string value for the attribute at <var>index</var>, but
+ * only if that string comes from an immediate value in an XML file. That
+ * is, this does not allow references to string resources, string
+ * attributes, or conversions from other types. As such, this method
+ * will only return strings for TypedArray objects that come from
+ * attributes in an XML file.
+ *
+ * @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 or is not
+ * an immediate string value.
+ */
+ public String getNonResourceString(int index) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_STRING) {
+ final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ if (cookie < 0) {
+ return mXml.getPooledString(
+ data[index+AssetManager.STYLE_DATA]).toString();
+ }
+ }
+ 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.
+ */
+ public boolean getBoolean(int index, boolean defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type >= TypedValue.TYPE_FIRST_INT
+ && type <= TypedValue.TYPE_LAST_INT) {
+ return data[index+AssetManager.STYLE_DATA] != 0;
+ }
+
+ TypedValue v = mValue;
+ if (getValueAt(index, v)) {
+ Log.w(Resources.TAG, "Converting to boolean: " + v);
+ return XmlUtils.convertValueToBoolean(
+ v.coerceToString(), defValue);
+ }
+ Log.w(Resources.TAG, "getBoolean of bad type: 0x"
+ + Integer.toHexString(type));
+ 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.
+ */
+ public int getInt(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type >= TypedValue.TYPE_FIRST_INT
+ && type <= TypedValue.TYPE_LAST_INT) {
+ return data[index+AssetManager.STYLE_DATA];
+ }
+
+ TypedValue v = mValue;
+ if (getValueAt(index, v)) {
+ Log.w(Resources.TAG, "Converting to int: " + v);
+ return XmlUtils.convertValueToInt(
+ v.coerceToString(), defValue);
+ }
+ Log.w(Resources.TAG, "getInt of bad type: 0x"
+ + Integer.toHexString(type));
+ 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..
+ */
+ public float getFloat(int index, float defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type == TypedValue.TYPE_FLOAT) {
+ return Float.intBitsToFloat(data[index+AssetManager.STYLE_DATA]);
+ } else if (type >= TypedValue.TYPE_FIRST_INT
+ && type <= TypedValue.TYPE_LAST_INT) {
+ return data[index+AssetManager.STYLE_DATA];
+ }
+
+ TypedValue v = mValue;
+ if (getValueAt(index, v)) {
+ Log.w(Resources.TAG, "Converting to float: " + v);
+ CharSequence str = v.coerceToString();
+ if (str != null) {
+ return Float.parseFloat(str.toString());
+ }
+ }
+ Log.w(Resources.TAG, "getFloat of bad type: 0x"
+ + Integer.toHexString(type));
+ 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.
+ */
+ public int getColor(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type >= TypedValue.TYPE_FIRST_INT
+ && type <= TypedValue.TYPE_LAST_INT) {
+ return data[index+AssetManager.STYLE_DATA];
+ } else if (type == TypedValue.TYPE_STRING) {
+ final TypedValue value = mValue;
+ if (getValueAt(index, value)) {
+ ColorStateList csl = mResources.loadColorStateList(
+ value, value.resourceId);
+ return csl.getDefaultColor();
+ }
+ return defValue;
+ }
+
+ throw new UnsupportedOperationException("Can't convert to color: type=0x"
+ + Integer.toHexString(type));
+ }
+
+ /**
+ * 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.
+ */
+ public ColorStateList getColorStateList(int index) {
+ final TypedValue value = mValue;
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ return mResources.loadColorStateList(value, value.resourceId);
+ }
+ 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.
+ */
+ public int getInteger(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type >= TypedValue.TYPE_FIRST_INT
+ && type <= TypedValue.TYPE_LAST_INT) {
+ return data[index+AssetManager.STYLE_DATA];
+ }
+
+ throw new UnsupportedOperationException("Can't convert to integer: type=0x"
+ + Integer.toHexString(type));
+ }
+
+ /**
+ * 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
+ */
+ public float getDimension(int index, float defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimension(
+ data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+ }
+
+ throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
+ + Integer.toHexString(type));
+ }
+
+ /**
+ * 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
+ */
+ public int getDimensionPixelOffset(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelOffset(
+ data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+ }
+
+ throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
+ + Integer.toHexString(type));
+ }
+
+ /**
+ * 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
+ */
+ public int getDimensionPixelSize(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelSize(
+ data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+ }
+
+ throw new UnsupportedOperationException("Can't convert to dimension: type=0x"
+ + Integer.toHexString(type));
+ }
+
+ /**
+ * 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.
+ */
+ public int getLayoutDimension(int index, String name) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type >= TypedValue.TYPE_FIRST_INT
+ && type <= TypedValue.TYPE_LAST_INT) {
+ return data[index+AssetManager.STYLE_DATA];
+ } else if (type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelSize(
+ data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+ }
+
+ throw new RuntimeException(getPositionDescription()
+ + ": You must supply a " + name + " attribute.");
+ }
+
+ /**
+ * 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 defValue The default value to return if this attribute is not
+ * default or contains the wrong type of data.
+ *
+ * @return Attribute dimension value multiplied by the appropriate
+ * metric and truncated to integer pixels.
+ */
+ public int getLayoutDimension(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type >= TypedValue.TYPE_FIRST_INT
+ && type <= TypedValue.TYPE_LAST_INT) {
+ return data[index+AssetManager.STYLE_DATA];
+ } else if (type == TypedValue.TYPE_DIMENSION) {
+ return TypedValue.complexToDimensionPixelSize(
+ data[index+AssetManager.STYLE_DATA], mResources.mMetrics);
+ }
+
+ return defValue;
+ }
+
+ /**
+ * 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.
+ */
+ public float getFraction(int index, int base, int pbase, float defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return defValue;
+ } else if (type == TypedValue.TYPE_FRACTION) {
+ return TypedValue.complexToFraction(
+ data[index+AssetManager.STYLE_DATA], base, pbase);
+ }
+
+ throw new UnsupportedOperationException("Can't convert to fraction: type=0x"
+ + Integer.toHexString(type));
+ }
+
+ /**
+ * 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.
+ */
+ public int getResourceId(int index, int defValue) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ if (data[index+AssetManager.STYLE_TYPE] != TypedValue.TYPE_NULL) {
+ final int resid = data[index+AssetManager.STYLE_RESOURCE_ID];
+ if (resid != 0) {
+ return resid;
+ }
+ }
+ 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.
+ */
+ public Drawable getDrawable(int index) {
+ final TypedValue value = mValue;
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (false) {
+ System.out.println("******************************************************************");
+ System.out.println("Got drawable resource: type="
+ + value.type
+ + " str=" + value.string
+ + " int=0x" + Integer.toHexString(value.data)
+ + " cookie=" + value.assetCookie);
+ System.out.println("******************************************************************");
+ }
+ return mResources.loadDrawable(value, value.resourceId);
+ }
+ return null;
+ }
+
+ /**
+ * 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.
+ */
+ public CharSequence[] getTextArray(int index) {
+ final TypedValue value = mValue;
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ if (false) {
+ System.out.println("******************************************************************");
+ System.out.println("Got drawable resource: type="
+ + value.type
+ + " str=" + value.string
+ + " int=0x" + Integer.toHexString(value.data)
+ + " cookie=" + value.assetCookie);
+ System.out.println("******************************************************************");
+ }
+ return mResources.getTextArray(value.resourceId);
+ }
+ 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.
+ */
+ public boolean getValue(int index, TypedValue outValue) {
+ return getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, outValue);
+ }
+
+ /**
+ * 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.
+ */
+ public boolean hasValue(int index) {
+ index *= AssetManager.STYLE_NUM_ENTRIES;
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ return type != TypedValue.TYPE_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.)
+ */
+ public TypedValue peekValue(int index) {
+ final TypedValue value = mValue;
+ if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
+ return value;
+ }
+ return null;
+ }
+
+ /**
+ * Returns a message about the parser state suitable for printing error messages.
+ */
+ public String getPositionDescription() {
+ return mXml != null ? mXml.getPositionDescription() : "<internal>";
+ }
+
+ /**
+ * Give back a previously retrieved StyledAttributes, for later re-use.
+ */
+ public void recycle() {
+ synchronized (mResources.mTmpValue) {
+ TypedArray cached = mResources.mCachedStyledAttributes;
+ if (cached == null || cached.mData.length < mData.length) {
+ mXml = null;
+ mResources.mCachedStyledAttributes = this;
+ }
+ }
+ }
+
+ private boolean getValueAt(int index, TypedValue outValue) {
+ final int[] data = mData;
+ final int type = data[index+AssetManager.STYLE_TYPE];
+ if (type == TypedValue.TYPE_NULL) {
+ return false;
+ }
+ outValue.type = type;
+ outValue.data = data[index+AssetManager.STYLE_DATA];
+ outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
+ outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
+ if (type == TypedValue.TYPE_STRING) {
+ outValue.string = loadStringValueAt(index);
+ }
+ return true;
+ }
+
+ private CharSequence loadStringValueAt(int index) {
+ final int[] data = mData;
+ final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
+ if (cookie < 0) {
+ if (mXml != null) {
+ return mXml.getPooledString(
+ data[index+AssetManager.STYLE_DATA]);
+ }
+ return null;
+ }
+ //System.out.println("Getting pooled from: " + v);
+ return mResources.mAssets.getPooledString(
+ cookie, data[index+AssetManager.STYLE_DATA]);
+ }
+
+ /*package*/ TypedArray(Resources resources, int[] data, int[] indices, int len) {
+ mResources = resources;
+ mData = data;
+ mIndices = indices;
+ mLength = len;
+ }
+
+ public String toString() {
+ return Arrays.toString(mData);
+ }
+}
\ No newline at end of file
diff --git a/core/java/android/content/res/XmlBlock.java b/core/java/android/content/res/XmlBlock.java
new file mode 100644
index 0000000..6336678
--- /dev/null
+++ b/core/java/android/content/res/XmlBlock.java
@@ -0,0 +1,515 @@
+/*
+ * Copyright (C) 2006 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 android.util.TypedValue;
+import com.android.internal.util.XmlUtils;
+
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * Wrapper around a compiled XML file.
+ *
+ * {@hide}
+ */
+final class XmlBlock {
+ private static final boolean DEBUG=false;
+
+ public XmlBlock(byte[] data) {
+ mAssets = null;
+ mNative = nativeCreate(data, 0, data.length);
+ mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
+ }
+
+ public XmlBlock(byte[] data, int offset, int size) {
+ mAssets = null;
+ mNative = nativeCreate(data, offset, size);
+ mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
+ }
+
+ public void close() {
+ synchronized (this) {
+ if (mOpen) {
+ mOpen = false;
+ decOpenCountLocked();
+ }
+ }
+ }
+
+ private void decOpenCountLocked() {
+ mOpenCount--;
+ if (mOpenCount == 0) {
+ nativeDestroy(mNative);
+ if (mAssets != null) {
+ mAssets.xmlBlockGone();
+ }
+ }
+ }
+
+ public XmlResourceParser newParser() {
+ synchronized (this) {
+ if (mNative != 0) {
+ return new Parser(nativeCreateParseState(mNative), this);
+ }
+ return null;
+ }
+ }
+
+ /*package*/ final class Parser implements XmlResourceParser {
+ Parser(int parseState, XmlBlock block) {
+ mParseState = parseState;
+ mBlock = block;
+ block.mOpenCount++;
+ }
+
+ public void setFeature(String name, boolean state) throws XmlPullParserException {
+ if (FEATURE_PROCESS_NAMESPACES.equals(name) && state) {
+ return;
+ }
+ if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name) && state) {
+ return;
+ }
+ throw new XmlPullParserException("Unsupported feature: " + name);
+ }
+ public boolean getFeature(String name) {
+ if (FEATURE_PROCESS_NAMESPACES.equals(name)) {
+ return true;
+ }
+ if (FEATURE_REPORT_NAMESPACE_ATTRIBUTES.equals(name)) {
+ return true;
+ }
+ return false;
+ }
+ public void setProperty(String name, Object value) throws XmlPullParserException {
+ throw new XmlPullParserException("setProperty() not supported");
+ }
+ public Object getProperty(String name) {
+ return null;
+ }
+ public void setInput(Reader in) throws XmlPullParserException {
+ throw new XmlPullParserException("setInput() not supported");
+ }
+ public void setInput(InputStream inputStream, String inputEncoding) throws XmlPullParserException {
+ throw new XmlPullParserException("setInput() not supported");
+ }
+ public void defineEntityReplacementText(String entityName, String replacementText) throws XmlPullParserException {
+ throw new XmlPullParserException("defineEntityReplacementText() not supported");
+ }
+ public String getNamespacePrefix(int pos) throws XmlPullParserException {
+ throw new XmlPullParserException("getNamespacePrefix() not supported");
+ }
+ public String getInputEncoding() {
+ return null;
+ }
+ public String getNamespace(String prefix) {
+ throw new RuntimeException("getNamespace() not supported");
+ }
+ public int getNamespaceCount(int depth) throws XmlPullParserException {
+ throw new XmlPullParserException("getNamespaceCount() not supported");
+ }
+ public String getPositionDescription() {
+ return "Binary XML file line #" + getLineNumber();
+ }
+ public String getNamespaceUri(int pos) throws XmlPullParserException {
+ throw new XmlPullParserException("getNamespaceUri() not supported");
+ }
+ public int getColumnNumber() {
+ return -1;
+ }
+ public int getDepth() {
+ return mDepth;
+ }
+ public String getText() {
+ int id = nativeGetText(mParseState);
+ return id >= 0 ? mStrings.get(id).toString() : null;
+ }
+ public int getLineNumber() {
+ return nativeGetLineNumber(mParseState);
+ }
+ public int getEventType() throws XmlPullParserException {
+ return mEventType;
+ }
+ public boolean isWhitespace() throws XmlPullParserException {
+ // whitespace was stripped by aapt.
+ return false;
+ }
+ public String getPrefix() {
+ throw new RuntimeException("getPrefix not supported");
+ }
+ public char[] getTextCharacters(int[] holderForStartAndLength) {
+ String txt = getText();
+ char[] chars = null;
+ if (txt != null) {
+ holderForStartAndLength[0] = 0;
+ holderForStartAndLength[1] = txt.length();
+ chars = new char[txt.length()];
+ txt.getChars(0, txt.length(), chars, 0);
+ }
+ return chars;
+ }
+ public String getNamespace() {
+ int id = nativeGetNamespace(mParseState);
+ return id >= 0 ? mStrings.get(id).toString() : "";
+ }
+ public String getName() {
+ int id = nativeGetName(mParseState);
+ return id >= 0 ? mStrings.get(id).toString() : null;
+ }
+ public String getAttributeNamespace(int index) {
+ int id = nativeGetAttributeNamespace(mParseState, index);
+ if (DEBUG) System.out.println("getAttributeNamespace of " + index + " = " + id);
+ if (id >= 0) return mStrings.get(id).toString();
+ else if (id == -1) return "";
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ public String getAttributeName(int index) {
+ int id = nativeGetAttributeName(mParseState, index);
+ if (DEBUG) System.out.println("getAttributeName of " + index + " = " + id);
+ if (id >= 0) return mStrings.get(id).toString();
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+ public String getAttributePrefix(int index) {
+ throw new RuntimeException("getAttributePrefix not supported");
+ }
+ public boolean isEmptyElementTag() throws XmlPullParserException {
+ // XXX Need to detect this.
+ return false;
+ }
+ public int getAttributeCount() {
+ return mEventType == START_TAG ? nativeGetAttributeCount(mParseState) : -1;
+ }
+ public String getAttributeValue(int index) {
+ int id = nativeGetAttributeStringValue(mParseState, index);
+ if (DEBUG) System.out.println("getAttributeValue of " + index + " = " + id);
+ if (id >= 0) return mStrings.get(id).toString();
+
+ // May be some other type... check and try to convert if so.
+ int t = nativeGetAttributeDataType(mParseState, index);
+ if (t == TypedValue.TYPE_NULL) {
+ throw new IndexOutOfBoundsException(String.valueOf(index));
+ }
+
+ int v = nativeGetAttributeData(mParseState, index);
+ return TypedValue.coerceToString(t, v);
+ }
+ public String getAttributeType(int index) {
+ return "CDATA";
+ }
+ public boolean isAttributeDefault(int index) {
+ return false;
+ }
+ public int nextToken() throws XmlPullParserException,IOException {
+ return next();
+ }
+ public String getAttributeValue(String namespace, String name) {
+ int idx = nativeGetAttributeIndex(mParseState, namespace, name);
+ if (idx >= 0) {
+ if (DEBUG) System.out.println("getAttributeName of "
+ + namespace + ":" + name + " index = " + idx);
+ if (DEBUG) System.out.println(
+ "Namespace=" + getAttributeNamespace(idx)
+ + "Name=" + getAttributeName(idx)
+ + ", Value=" + getAttributeValue(idx));
+ return getAttributeValue(idx);
+ }
+ return null;
+ }
+ public int next() throws XmlPullParserException,IOException {
+ if (!mStarted) {
+ mStarted = true;
+ return START_DOCUMENT;
+ }
+ if (mParseState == 0) {
+ return END_DOCUMENT;
+ }
+ int ev = nativeNext(mParseState);
+ if (mDecNextDepth) {
+ mDepth--;
+ mDecNextDepth = false;
+ }
+ switch (ev) {
+ case START_TAG:
+ mDepth++;
+ break;
+ case END_TAG:
+ mDecNextDepth = true;
+ break;
+ }
+ mEventType = ev;
+ if (ev == END_DOCUMENT) {
+ // Automatically close the parse when we reach the end of
+ // a document, since the standard XmlPullParser interface
+ // doesn't have such an API so most clients will leave us
+ // dangling.
+ close();
+ }
+ return ev;
+ }
+ public void require(int type, String namespace, String name) throws XmlPullParserException,IOException {
+ if (type != getEventType()
+ || (namespace != null && !namespace.equals( getNamespace () ) )
+ || (name != null && !name.equals( getName() ) ) )
+ throw new XmlPullParserException( "expected "+ TYPES[ type ]+getPositionDescription());
+ }
+ public String nextText() throws XmlPullParserException,IOException {
+ if(getEventType() != START_TAG) {
+ throw new XmlPullParserException(
+ getPositionDescription()
+ + ": parser must be on START_TAG to read next text", this, null);
+ }
+ int eventType = next();
+ if(eventType == TEXT) {
+ String result = getText();
+ eventType = next();
+ if(eventType != END_TAG) {
+ throw new XmlPullParserException(
+ getPositionDescription()
+ + ": event TEXT it must be immediately followed by END_TAG", this, null);
+ }
+ return result;
+ } else if(eventType == END_TAG) {
+ return "";
+ } else {
+ throw new XmlPullParserException(
+ getPositionDescription()
+ + ": parser must be on START_TAG or TEXT to read text", this, null);
+ }
+ }
+ public int nextTag() throws XmlPullParserException,IOException {
+ int eventType = next();
+ if(eventType == TEXT && isWhitespace()) { // skip whitespace
+ eventType = next();
+ }
+ if (eventType != START_TAG && eventType != END_TAG) {
+ throw new XmlPullParserException(
+ getPositionDescription()
+ + ": expected start or end tag", this, null);
+ }
+ return eventType;
+ }
+
+ public int getAttributeNameResource(int index) {
+ return nativeGetAttributeResource(mParseState, index);
+ }
+
+ public int getAttributeListValue(String namespace, String attribute,
+ String[] options, int defaultValue) {
+ int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+ if (idx >= 0) {
+ return getAttributeListValue(idx, options, defaultValue);
+ }
+ return defaultValue;
+ }
+ public boolean getAttributeBooleanValue(String namespace, String attribute,
+ boolean defaultValue) {
+ int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+ if (idx >= 0) {
+ return getAttributeBooleanValue(idx, defaultValue);
+ }
+ return defaultValue;
+ }
+ public int getAttributeResourceValue(String namespace, String attribute,
+ int defaultValue) {
+ int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+ if (idx >= 0) {
+ return getAttributeResourceValue(idx, defaultValue);
+ }
+ return defaultValue;
+ }
+ public int getAttributeIntValue(String namespace, String attribute,
+ int defaultValue) {
+ int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+ if (idx >= 0) {
+ return getAttributeIntValue(idx, defaultValue);
+ }
+ return defaultValue;
+ }
+ public int getAttributeUnsignedIntValue(String namespace, String attribute,
+ int defaultValue)
+ {
+ int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+ if (idx >= 0) {
+ return getAttributeUnsignedIntValue(idx, defaultValue);
+ }
+ return defaultValue;
+ }
+ public float getAttributeFloatValue(String namespace, String attribute,
+ float defaultValue) {
+ int idx = nativeGetAttributeIndex(mParseState, namespace, attribute);
+ if (idx >= 0) {
+ return getAttributeFloatValue(idx, defaultValue);
+ }
+ return defaultValue;
+ }
+
+ public int getAttributeListValue(int idx,
+ String[] options, int defaultValue) {
+ int t = nativeGetAttributeDataType(mParseState, idx);
+ int v = nativeGetAttributeData(mParseState, idx);
+ if (t == TypedValue.TYPE_STRING) {
+ return XmlUtils.convertValueToList(
+ mStrings.get(v), options, defaultValue);
+ }
+ return v;
+ }
+ public boolean getAttributeBooleanValue(int idx,
+ boolean defaultValue) {
+ int t = nativeGetAttributeDataType(mParseState, idx);
+ // Note: don't attempt to convert any other types, because
+ // we want to count on appt doing the conversion for us.
+ if (t >= TypedValue.TYPE_FIRST_INT &&
+ t <= TypedValue.TYPE_LAST_INT) {
+ return nativeGetAttributeData(mParseState, idx) != 0;
+ }
+ return defaultValue;
+ }
+ public int getAttributeResourceValue(int idx, int defaultValue) {
+ int t = nativeGetAttributeDataType(mParseState, idx);
+ // Note: don't attempt to convert any other types, because
+ // we want to count on appt doing the conversion for us.
+ if (t == TypedValue.TYPE_REFERENCE) {
+ return nativeGetAttributeData(mParseState, idx);
+ }
+ return defaultValue;
+ }
+ public int getAttributeIntValue(int idx, int defaultValue) {
+ int t = nativeGetAttributeDataType(mParseState, idx);
+ // Note: don't attempt to convert any other types, because
+ // we want to count on appt doing the conversion for us.
+ if (t >= TypedValue.TYPE_FIRST_INT &&
+ t <= TypedValue.TYPE_LAST_INT) {
+ return nativeGetAttributeData(mParseState, idx);
+ }
+ return defaultValue;
+ }
+ public int getAttributeUnsignedIntValue(int idx, int defaultValue) {
+ int t = nativeGetAttributeDataType(mParseState, idx);
+ // Note: don't attempt to convert any other types, because
+ // we want to count on appt doing the conversion for us.
+ if (t >= TypedValue.TYPE_FIRST_INT &&
+ t <= TypedValue.TYPE_LAST_INT) {
+ return nativeGetAttributeData(mParseState, idx);
+ }
+ return defaultValue;
+ }
+ public float getAttributeFloatValue(int idx, float defaultValue) {
+ int t = nativeGetAttributeDataType(mParseState, idx);
+ // Note: don't attempt to convert any other types, because
+ // we want to count on appt doing the conversion for us.
+ if (t == TypedValue.TYPE_FLOAT) {
+ return Float.intBitsToFloat(
+ nativeGetAttributeData(mParseState, idx));
+ }
+ throw new RuntimeException("not a float!");
+ }
+
+ public String getIdAttribute() {
+ int id = nativeGetIdAttribute(mParseState);
+ return id >= 0 ? mStrings.get(id).toString() : null;
+ }
+ public String getClassAttribute() {
+ int id = nativeGetClassAttribute(mParseState);
+ return id >= 0 ? mStrings.get(id).toString() : null;
+ }
+
+ public int getIdAttributeResourceValue(int defaultValue) {
+ //todo: create and use native method
+ return getAttributeResourceValue(null, "id", defaultValue);
+ }
+
+ public int getStyleAttribute() {
+ return nativeGetStyleAttribute(mParseState);
+ }
+
+ public void close() {
+ synchronized (mBlock) {
+ if (mParseState != 0) {
+ nativeDestroyParseState(mParseState);
+ mParseState = 0;
+ mBlock.decOpenCountLocked();
+ }
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ close();
+ }
+
+ /*package*/ final CharSequence getPooledString(int id) {
+ return mStrings.get(id);
+ }
+
+ /*package*/ int mParseState;
+ private final XmlBlock mBlock;
+ private boolean mStarted = false;
+ private boolean mDecNextDepth = false;
+ private int mDepth = 0;
+ private int mEventType = START_DOCUMENT;
+ }
+
+ protected void finalize() throws Throwable {
+ close();
+ }
+
+ /**
+ * Create from an existing xml block native object. This is
+ * -extremely- dangerous -- only use it if you absolutely know what you
+ * are doing! The given native object must exist for the entire lifetime
+ * of this newly creating XmlBlock.
+ */
+ XmlBlock(AssetManager assets, int xmlBlock) {
+ mAssets = assets;
+ mNative = xmlBlock;
+ mStrings = new StringBlock(nativeGetStringBlock(xmlBlock), false);
+ }
+
+ private final AssetManager mAssets;
+ private final int mNative;
+ private final StringBlock mStrings;
+ private boolean mOpen = true;
+ private int mOpenCount = 1;
+
+ private static final native int nativeCreate(byte[] data,
+ int offset,
+ int size);
+ private static final native int nativeGetStringBlock(int obj);
+
+ private static final native int nativeCreateParseState(int obj);
+ private static final native int nativeNext(int state);
+ private static final native int nativeGetNamespace(int state);
+ private static final native int nativeGetName(int state);
+ private static final native int nativeGetText(int state);
+ private static final native int nativeGetLineNumber(int state);
+ private static final native int nativeGetAttributeCount(int state);
+ private static final native int nativeGetAttributeNamespace(int state, int idx);
+ private static final native int nativeGetAttributeName(int state, int idx);
+ private static final native int nativeGetAttributeResource(int state, int idx);
+ private static final native int nativeGetAttributeDataType(int state, int idx);
+ private static final native int nativeGetAttributeData(int state, int idx);
+ private static final native int nativeGetAttributeStringValue(int state, int idx);
+ private static final native int nativeGetIdAttribute(int state);
+ private static final native int nativeGetClassAttribute(int state);
+ private static final native int nativeGetStyleAttribute(int state);
+ private static final native int nativeGetAttributeIndex(int state, String namespace, String name);
+ private static final native void nativeDestroyParseState(int state);
+
+ private static final native void nativeDestroy(int obj);
+}
diff --git a/core/java/android/content/res/XmlResourceParser.java b/core/java/android/content/res/XmlResourceParser.java
new file mode 100644
index 0000000..c59e6d4
--- /dev/null
+++ b/core/java/android/content/res/XmlResourceParser.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2006 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 org.xmlpull.v1.XmlPullParser;
+
+import android.util.AttributeSet;
+
+/**
+ * The XML parsing interface returned for an XML resource. This is a standard
+ * XmlPullParser interface, as well as an extended AttributeSet interface and
+ * an additional close() method on this interface for the client to indicate
+ * when it is done reading the resource.
+ */
+public interface XmlResourceParser extends XmlPullParser, AttributeSet {
+ /**
+ * Close this interface to the resource. Calls on the interface are no
+ * longer value after this call.
+ */
+ public void close();
+}
+
diff --git a/core/java/android/content/res/package.html b/core/java/android/content/res/package.html
new file mode 100644
index 0000000..bb09dc7
--- /dev/null
+++ b/core/java/android/content/res/package.html
@@ -0,0 +1,8 @@
+<HTML>
+<BODY>
+Contains classes for accessing application resources,
+such as raw asset files, colors, drawables, media or other other files
+in the package, plus important device configuration details
+(orientation, input types, etc.) that affect how the application may behave.
+</BODY>
+</HTML>
\ No newline at end of file