blob: 8ebe0932c214053c1d7b540c97b6fee5607507cc [file] [log] [blame]
/*
* 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. Returns
* the cookie of the added asset, or 0 on 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 screenLayout, 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 = 6;
/*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*/ static final int STYLE_DENSITY = 5;
/*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();
}
}
}