M7350v1_en_gpl

This commit is contained in:
T
2024-09-09 08:52:07 +00:00
commit f9cc65cfda
65988 changed files with 26357421 additions and 0 deletions
@@ -0,0 +1,440 @@
/*
* 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.MemoryFile;
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;
import java.io.InputStream;
import java.nio.channels.FileChannel;
/**
* 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();
}
/**
* Checks whether this file descriptor is for a memory file.
*/
private boolean isMemoryFile() throws IOException {
try {
return MemoryFile.isMemoryFile(mFd.getFileDescriptor());
} catch (IOException e) {
return false;
}
}
/**
* 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 (isMemoryFile()) {
if (mLength > Integer.MAX_VALUE) {
throw new IOException("File length too large for a memory file: " + mLength);
}
return new AutoCloseMemoryFileInputStream(mFd, (int)mLength);
}
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 input stream that reads from a MemoryFile and closes it when the stream is closed.
* This extends FileInputStream just because {@link #createInputStream} returns
* a FileInputStream. All the FileInputStream methods are
* overridden to use the MemoryFile instead.
*/
private static class AutoCloseMemoryFileInputStream extends FileInputStream {
private ParcelFileDescriptor mParcelFd;
private MemoryFile mFile;
private InputStream mStream;
public AutoCloseMemoryFileInputStream(ParcelFileDescriptor fd, int length)
throws IOException {
super(fd.getFileDescriptor());
mParcelFd = fd;
mFile = new MemoryFile(fd.getFileDescriptor(), length, "r");
mStream = mFile.getInputStream();
}
@Override
public int available() throws IOException {
return mStream.available();
}
@Override
public void close() throws IOException {
mParcelFd.close(); // must close ParcelFileDescriptor, not just the file descriptor,
// since it could be a subclass of ParcelFileDescriptor.
// E.g. ContentResolver.ParcelFileDescriptorInner.close() releases
// a content provider
mFile.close(); // to unmap the memory file from the address space.
mStream.close(); // doesn't actually do anything
}
@Override
public FileChannel getChannel() {
return null;
}
@Override
public int read() throws IOException {
return mStream.read();
}
@Override
public int read(byte[] buffer, int offset, int count) throws IOException {
return mStream.read(buffer, offset, count);
}
@Override
public int read(byte[] buffer) throws IOException {
return mStream.read(buffer);
}
@Override
public long skip(long count) throws IOException {
return mStream.skip(count);
}
}
/**
* 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];
}
};
/**
* Creates an AssetFileDescriptor from a memory file.
*
* @hide
*/
public static AssetFileDescriptor fromMemoryFile(MemoryFile memoryFile)
throws IOException {
ParcelFileDescriptor fd = memoryFile.getParcelFileDescriptor();
return new AssetFileDescriptor(fd, 0, memoryFile.length());
}
}
@@ -0,0 +1,768 @@
/*
* 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.HashMap;
/**
* 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 boolean DEBUG_REFS = false;
private static final Object sSync = new Object();
private static AssetManager sSystem = null;
private final TypedValue mValue = new TypedValue();
private final long[] mOffsets = new long[2];
// For communication with native code.
private int mObject;
private int mNObject; // used by the NDK
private StringBlock mStringBlocks[] = null;
private int mNumRefs = 1;
private boolean mOpen = true;
private HashMap<Integer, RuntimeException> mRefStacks;
/**
* 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 (this) {
if (DEBUG_REFS) {
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
init();
if (localLOGV) Log.v(TAG, "New asset manager: " + this);
ensureSystemAssets();
}
}
private static void ensureSystemAssets() {
synchronized (sSync) {
if (sSystem == null) {
AssetManager system = new AssetManager(true);
system.makeStringBlocks(false);
sSystem = system;
}
}
}
private AssetManager(boolean isSystem) {
if (DEBUG_REFS) {
synchronized (this) {
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
}
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 sSystem;
}
/**
* Close this asset manager.
*/
public void close() {
synchronized(this) {
//System.out.println("Release: num=" + mNumRefs
// + ", released=" + mReleased);
if (mOpen) {
mOpen = false;
decRefsLocked(this.hashCode());
}
}
}
/**
* Retrieve the string value associated with a particular resource
* identifier for the current configuration / skin.
*/
/*package*/ final CharSequence getResourceText(int ident) {
synchronized (this) {
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 (this) {
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 (this) {
if (mStringBlocks == null) {
makeStringBlocks(true);
}
}
}
}
private final void makeStringBlocks(boolean copyFromSystem) {
final int sysNum = copyFromSystem ? sSystem.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] = sSystem.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 (this) {
if (!mOpen) {
throw new RuntimeException("Assetmanager has been closed");
}
int asset = openAsset(fileName, accessMode);
if (asset != 0) {
AssetInputStream res = new AssetInputStream(asset);
incRefsLocked(res.hashCode());
return res;
}
}
throw new FileNotFoundException("Asset file: " + fileName);
}
public final AssetFileDescriptor openFd(String fileName)
throws IOException {
synchronized (this) {
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 (this) {
if (!mOpen) {
throw new RuntimeException("Assetmanager has been closed");
}
int asset = openNonAssetNative(cookie, fileName, accessMode);
if (asset != 0) {
AssetInputStream res = new AssetInputStream(asset);
incRefsLocked(res.hashCode());
return res;
}
}
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 (this) {
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 (this) {
if (!mOpen) {
throw new RuntimeException("Assetmanager has been closed");
}
int xmlBlock = openXmlAssetNative(cookie, fileName);
if (xmlBlock != 0) {
XmlBlock res = new XmlBlock(this, xmlBlock);
incRefsLocked(res.hashCode());
return res;
}
}
throw new FileNotFoundException("Asset XML file: " + fileName);
}
/*package*/ void xmlBlockGone(int id) {
synchronized (this) {
decRefsLocked(id);
}
}
/*package*/ final int createTheme() {
synchronized (this) {
if (!mOpen) {
throw new RuntimeException("Assetmanager has been closed");
}
int res = newTheme();
incRefsLocked(res);
return res;
}
}
/*package*/ final void releaseTheme(int theme) {
synchronized (this) {
deleteTheme(theme);
decRefsLocked(theme);
}
}
protected void finalize() throws Throwable {
try {
if (DEBUG_REFS && mNumRefs != 0) {
Log.w(TAG, "AssetManager " + this
+ " finalized with non-zero refs: " + mNumRefs);
if (mRefStacks != null) {
for (RuntimeException e : mRefStacks.values()) {
Log.w(TAG, "Reference from here", e);
}
}
}
destroy();
} finally {
super.finalize();
}
}
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.this) {
if (mAsset != 0) {
destroyAsset(mAsset);
mAsset = 0;
decRefsLocked(hashCode());
}
}
}
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);
/**
* Add multiple sets of assets to the asset manager at once. See
* {@link #addAssetPath(String)} for more information. Returns array of
* cookies for each added asset with 0 indicating failure, or null if
* the input array of paths is null.
* {@hide}
*/
public final int[] addAssetPaths(String[] paths) {
if (paths == null) {
return null;
}
int[] cookies = new int[paths.length];
for (int i = 0; i < paths.length; i++) {
cookies[i] = addAssetPath(paths[i]);
}
return cookies;
}
/**
* 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 uiMode, 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 String getAssetAllocations();
/**
* {@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 incRefsLocked(int id) {
if (DEBUG_REFS) {
if (mRefStacks == null) {
mRefStacks = new HashMap<Integer, RuntimeException>();
RuntimeException ex = new RuntimeException();
ex.fillInStackTrace();
mRefStacks.put(this.hashCode(), ex);
}
}
mNumRefs++;
}
private final void decRefsLocked(int id) {
if (DEBUG_REFS && mRefStacks != null) {
mRefStacks.remove(id);
}
mNumRefs--;
//System.out.println("Dec streams: mNumRefs=" + mNumRefs
// + " mReleased=" + mReleased);
if (mNumRefs == 0) {
destroy();
}
}
}
@@ -0,0 +1,328 @@
/*
* 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>
* &lt;selector xmlns:android="http://schemas.android.com/apk/res/android"&gt;
* &lt;item android:state_focused="true" android:color="@color/testcolor1"/&gt;
* &lt;item android:state_pressed="true" android:state_enabled="false" android:color="@color/testcolor2" /&gt;
* &lt;item android:state_enabled="false" android:color="@color/testcolor3" /&gt;
* &lt;item android:color="@color/testcolor5"/&gt;
* &lt;/selector&gt;
* </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.
* <p>For more information, see the guide to <a
* href="{@docRoot}guide/topics/resources/color-list-resource.html">Color State
* List Resource</a>.</p>
*/
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.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);
}
};
}
@@ -0,0 +1,422 @@
/*
* 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.content.pm.ApplicationInfo;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
/**
* CompatibilityInfo class keeps the information about compatibility mode that the application is
* running under.
*
* {@hide}
*/
public class CompatibilityInfo {
private static final boolean DBG = false;
private static final String TAG = "CompatibilityInfo";
/** default compatibility info object for compatible applications */
public static final CompatibilityInfo DEFAULT_COMPATIBILITY_INFO = new CompatibilityInfo() {
@Override
public void setExpandable(boolean expandable) {
throw new UnsupportedOperationException("trying to change default compatibility info");
}
};
/**
* The default width of the screen in portrait mode.
*/
public static final int DEFAULT_PORTRAIT_WIDTH = 320;
/**
* The default height of the screen in portrait mode.
*/
public static final int DEFAULT_PORTRAIT_HEIGHT = 480;
/**
* A compatibility flags
*/
private int mCompatibilityFlags;
/**
* A flag mask to tell if the application needs scaling (when mApplicationScale != 1.0f)
* {@see compatibilityFlag}
*/
private static final int SCALING_REQUIRED = 1;
/**
* A flag mask to indicates that the application can expand over the original size.
* The flag is set to true if
* 1) Application declares its expandable in manifest file using <supports-screens> or
* 2) Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set
* {@see compatibilityFlag}
*/
private static final int EXPANDABLE = 2;
/**
* A flag mask to tell if the application is configured to be expandable. This differs
* from EXPANDABLE in that the application that is not expandable will be
* marked as expandable if Configuration.SCREENLAYOUT_COMPAT_NEEDED is not set.
*/
private static final int CONFIGURED_EXPANDABLE = 4;
/**
* A flag mask to indicates that the application supports large screens.
* The flag is set to true if
* 1) Application declares it supports large screens in manifest file using <supports-screens> or
* 2) The screen size is not large
* {@see compatibilityFlag}
*/
private static final int LARGE_SCREENS = 8;
/**
* A flag mask to tell if the application supports large screens. This differs
* from LARGE_SCREENS in that the application that does not support large
* screens will be marked as supporting them if the current screen is not
* large.
*/
private static final int CONFIGURED_LARGE_SCREENS = 16;
/**
* A flag mask to indicates that the application supports xlarge screens.
* The flag is set to true if
* 1) Application declares it supports xlarge screens in manifest file using <supports-screens> or
* 2) The screen size is not xlarge
* {@see compatibilityFlag}
*/
private static final int XLARGE_SCREENS = 32;
/**
* A flag mask to tell if the application supports xlarge screens. This differs
* from XLARGE_SCREENS in that the application that does not support xlarge
* screens will be marked as supporting them if the current screen is not
* xlarge.
*/
private static final int CONFIGURED_XLARGE_SCREENS = 64;
/**
* The effective screen density we have selected for this application.
*/
public final int applicationDensity;
/**
* Application's scale.
*/
public final float applicationScale;
/**
* Application's inverted scale.
*/
public final float applicationInvertedScale;
/**
* The flags from ApplicationInfo.
*/
public final int appFlags;
public CompatibilityInfo(ApplicationInfo appInfo) {
appFlags = appInfo.flags;
if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS) != 0) {
// Saying you support large screens also implies you support xlarge
// screens; there is no compatibility mode for a large app on an
// xlarge screen.
mCompatibilityFlags |= LARGE_SCREENS | CONFIGURED_LARGE_SCREENS
| XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
| EXPANDABLE | CONFIGURED_EXPANDABLE;
}
if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS) != 0) {
mCompatibilityFlags |= XLARGE_SCREENS | CONFIGURED_XLARGE_SCREENS
| EXPANDABLE | CONFIGURED_EXPANDABLE;
}
if ((appInfo.flags & ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS) != 0) {
mCompatibilityFlags |= EXPANDABLE | CONFIGURED_EXPANDABLE;
}
if ((appInfo.flags & ApplicationInfo.FLAG_SUPPORTS_SCREEN_DENSITIES) != 0) {
applicationDensity = DisplayMetrics.DENSITY_DEVICE;
applicationScale = 1.0f;
applicationInvertedScale = 1.0f;
} else {
applicationDensity = DisplayMetrics.DENSITY_DEFAULT;
applicationScale = DisplayMetrics.DENSITY_DEVICE
/ (float) DisplayMetrics.DENSITY_DEFAULT;
applicationInvertedScale = 1.0f / applicationScale;
mCompatibilityFlags |= SCALING_REQUIRED;
}
}
private CompatibilityInfo(int appFlags, int compFlags,
int dens, float scale, float invertedScale) {
this.appFlags = appFlags;
mCompatibilityFlags = compFlags;
applicationDensity = dens;
applicationScale = scale;
applicationInvertedScale = invertedScale;
}
private CompatibilityInfo() {
this(ApplicationInfo.FLAG_SUPPORTS_SMALL_SCREENS
| ApplicationInfo.FLAG_SUPPORTS_NORMAL_SCREENS
| ApplicationInfo.FLAG_SUPPORTS_LARGE_SCREENS
| ApplicationInfo.FLAG_SUPPORTS_XLARGE_SCREENS
| ApplicationInfo.FLAG_RESIZEABLE_FOR_SCREENS,
EXPANDABLE | CONFIGURED_EXPANDABLE,
DisplayMetrics.DENSITY_DEVICE,
1.0f,
1.0f);
}
/**
* Returns the copy of this instance.
*/
public CompatibilityInfo copy() {
CompatibilityInfo info = new CompatibilityInfo(appFlags, mCompatibilityFlags,
applicationDensity, applicationScale, applicationInvertedScale);
return info;
}
/**
* Sets expandable bit in the compatibility flag.
*/
public void setExpandable(boolean expandable) {
if (expandable) {
mCompatibilityFlags |= CompatibilityInfo.EXPANDABLE;
} else {
mCompatibilityFlags &= ~CompatibilityInfo.EXPANDABLE;
}
}
/**
* Sets large screen bit in the compatibility flag.
*/
public void setLargeScreens(boolean expandable) {
if (expandable) {
mCompatibilityFlags |= CompatibilityInfo.LARGE_SCREENS;
} else {
mCompatibilityFlags &= ~CompatibilityInfo.LARGE_SCREENS;
}
}
/**
* Sets large screen bit in the compatibility flag.
*/
public void setXLargeScreens(boolean expandable) {
if (expandable) {
mCompatibilityFlags |= CompatibilityInfo.XLARGE_SCREENS;
} else {
mCompatibilityFlags &= ~CompatibilityInfo.XLARGE_SCREENS;
}
}
/**
* @return true if the application is configured to be expandable.
*/
public boolean isConfiguredExpandable() {
return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_EXPANDABLE) != 0;
}
/**
* @return true if the application is configured to be expandable.
*/
public boolean isConfiguredLargeScreens() {
return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_LARGE_SCREENS) != 0;
}
/**
* @return true if the application is configured to be expandable.
*/
public boolean isConfiguredXLargeScreens() {
return (mCompatibilityFlags & CompatibilityInfo.CONFIGURED_XLARGE_SCREENS) != 0;
}
/**
* @return true if the scaling is required
*/
public boolean isScalingRequired() {
return (mCompatibilityFlags & SCALING_REQUIRED) != 0;
}
public boolean supportsScreen() {
return (mCompatibilityFlags & (EXPANDABLE|LARGE_SCREENS))
== (EXPANDABLE|LARGE_SCREENS);
}
@Override
public String toString() {
return "CompatibilityInfo{scale=" + applicationScale +
", supports screen=" + supportsScreen() + "}";
}
/**
* Returns the translator which translates the coordinates in compatibility mode.
* @param params the window's parameter
*/
public Translator getTranslator() {
return isScalingRequired() ? new Translator() : null;
}
/**
* A helper object to translate the screen and window coordinates back and forth.
* @hide
*/
public class Translator {
final public float applicationScale;
final public float applicationInvertedScale;
private Rect mContentInsetsBuffer = null;
private Rect mVisibleInsetsBuffer = null;
Translator(float applicationScale, float applicationInvertedScale) {
this.applicationScale = applicationScale;
this.applicationInvertedScale = applicationInvertedScale;
}
Translator() {
this(CompatibilityInfo.this.applicationScale,
CompatibilityInfo.this.applicationInvertedScale);
}
/**
* Translate the screen rect to the application frame.
*/
public void translateRectInScreenToAppWinFrame(Rect rect) {
rect.scale(applicationInvertedScale);
}
/**
* Translate the region in window to screen.
*/
public void translateRegionInWindowToScreen(Region transparentRegion) {
transparentRegion.scale(applicationScale);
}
/**
* Apply translation to the canvas that is necessary to draw the content.
*/
public void translateCanvas(Canvas canvas) {
if (applicationScale == 1.5f) {
/* When we scale for compatibility, we can put our stretched
bitmaps and ninepatches on exacty 1/2 pixel boundaries,
which can give us inconsistent drawing due to imperfect
float precision in the graphics engine's inverse matrix.
As a work-around, we translate by a tiny amount to avoid
landing on exact pixel centers and boundaries, giving us
the slop we need to draw consistently.
This constant is meant to resolve to 1/255 after it is
scaled by 1.5 (applicationScale). Note, this is just a guess
as to what is small enough not to create its own artifacts,
and big enough to avoid the precision problems. Feel free
to experiment with smaller values as you choose.
*/
final float tinyOffset = 2.0f / (3 * 255);
canvas.translate(tinyOffset, tinyOffset);
}
canvas.scale(applicationScale, applicationScale);
}
/**
* Translate the motion event captured on screen to the application's window.
*/
public void translateEventInScreenToAppWindow(MotionEvent event) {
event.scale(applicationInvertedScale);
}
/**
* Translate the window's layout parameter, from application's view to
* Screen's view.
*/
public void translateWindowLayout(WindowManager.LayoutParams params) {
params.scale(applicationScale);
}
/**
* Translate a Rect in application's window to screen.
*/
public void translateRectInAppWindowToScreen(Rect rect) {
rect.scale(applicationScale);
}
/**
* Translate a Rect in screen coordinates into the app window's coordinates.
*/
public void translateRectInScreenToAppWindow(Rect rect) {
rect.scale(applicationInvertedScale);
}
/**
* Translate the location of the sub window.
* @param params
*/
public void translateLayoutParamsInAppWindowToScreen(LayoutParams params) {
params.scale(applicationScale);
}
/**
* Translate the content insets in application window to Screen. This uses
* the internal buffer for content insets to avoid extra object allocation.
*/
public Rect getTranslatedContentInsets(Rect contentInsets) {
if (mContentInsetsBuffer == null) mContentInsetsBuffer = new Rect();
mContentInsetsBuffer.set(contentInsets);
translateRectInAppWindowToScreen(mContentInsetsBuffer);
return mContentInsetsBuffer;
}
/**
* Translate the visible insets in application window to Screen. This uses
* the internal buffer for content insets to avoid extra object allocation.
*/
public Rect getTranslatedVisbileInsets(Rect visibleInsets) {
if (mVisibleInsetsBuffer == null) mVisibleInsetsBuffer = new Rect();
mVisibleInsetsBuffer.set(visibleInsets);
translateRectInAppWindowToScreen(mVisibleInsetsBuffer);
return mVisibleInsetsBuffer;
}
}
/**
* Returns the frame Rect for applications runs under compatibility mode.
*
* @param dm the display metrics used to compute the frame size.
* @param orientation the orientation of the screen.
* @param outRect the output parameter which will contain the result.
*/
public static void updateCompatibleScreenFrame(DisplayMetrics dm, int orientation,
Rect outRect) {
int width = dm.widthPixels;
int portraitHeight = (int) (DEFAULT_PORTRAIT_HEIGHT * dm.density + 0.5f);
int portraitWidth = (int) (DEFAULT_PORTRAIT_WIDTH * dm.density + 0.5f);
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
int xOffset = (width - portraitHeight) / 2 ;
outRect.set(xOffset, 0, xOffset + portraitHeight, portraitWidth);
} else {
int xOffset = (width - portraitWidth) / 2 ;
outRect.set(xOffset, 0, xOffset + portraitWidth, portraitHeight);
}
}
}
+21
View File
@@ -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;
@@ -0,0 +1,664 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.res;
import 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. This is hidden because it is really
* questionable whether this is the right way to expose the functionality.
* @hide
*/
public boolean userSetLocale;
public static final int SCREENLAYOUT_SIZE_MASK = 0x0f;
public static final int SCREENLAYOUT_SIZE_UNDEFINED = 0x00;
public static final int SCREENLAYOUT_SIZE_SMALL = 0x01;
public static final int SCREENLAYOUT_SIZE_NORMAL = 0x02;
public static final int SCREENLAYOUT_SIZE_LARGE = 0x03;
public static final int SCREENLAYOUT_SIZE_XLARGE = 0x04;
public static final int SCREENLAYOUT_LONG_MASK = 0x30;
public static final int SCREENLAYOUT_LONG_UNDEFINED = 0x00;
public static final int SCREENLAYOUT_LONG_NO = 0x10;
public static final int SCREENLAYOUT_LONG_YES = 0x20;
/**
* Special flag we generate to indicate that the screen layout requires
* us to use a compatibility mode for apps that are not modern layout
* aware.
* @hide
*/
public static final int SCREENLAYOUT_COMPAT_NEEDED = 0x10000000;
/**
* Bit mask of overall layout of the screen. Currently there are two
* fields:
* <p>The {@link #SCREENLAYOUT_SIZE_MASK} bits define the overall size
* of the screen. They may be one of
* {@link #SCREENLAYOUT_SIZE_SMALL}, {@link #SCREENLAYOUT_SIZE_NORMAL},
* {@link #SCREENLAYOUT_SIZE_LARGE}.
*
* <p>The {@link #SCREENLAYOUT_LONG_MASK} defines whether the screen
* is wider/taller than normal. They may be one of
* {@link #SCREENLAYOUT_LONG_NO} or {@link #SCREENLAYOUT_LONG_YES}.
*/
public int screenLayout;
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_NOKEYS}, {@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_NONAV}, {@link #NAVIGATION_DPAD},
* {@link #NAVIGATION_TRACKBALL}, {@link #NAVIGATION_WHEEL}.
*/
public int navigation;
public static final int NAVIGATIONHIDDEN_UNDEFINED = 0;
public static final int NAVIGATIONHIDDEN_NO = 1;
public static final int NAVIGATIONHIDDEN_YES = 2;
/**
* A flag indicating whether any 5-way or DPAD navigation available.
* This will be set on a device with a mechanism to hide the navigation
* controls from the user, when that mechanism is closed. One of:
* {@link #NAVIGATIONHIDDEN_NO}, {@link #NAVIGATIONHIDDEN_YES}.
*/
public int navigationHidden;
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;
public static final int UI_MODE_TYPE_MASK = 0x0f;
public static final int UI_MODE_TYPE_UNDEFINED = 0x00;
public static final int UI_MODE_TYPE_NORMAL = 0x01;
public static final int UI_MODE_TYPE_DESK = 0x02;
public static final int UI_MODE_TYPE_CAR = 0x03;
public static final int UI_MODE_NIGHT_MASK = 0x30;
public static final int UI_MODE_NIGHT_UNDEFINED = 0x00;
public static final int UI_MODE_NIGHT_NO = 0x10;
public static final int UI_MODE_NIGHT_YES = 0x20;
/**
* Bit mask of the ui mode. Currently there are two fields:
* <p>The {@link #UI_MODE_TYPE_MASK} bits define the overall ui mode of the
* device. They may be one of {@link #UI_MODE_TYPE_UNDEFINED},
* {@link #UI_MODE_TYPE_NORMAL}, {@link #UI_MODE_TYPE_DESK},
* or {@link #UI_MODE_TYPE_CAR}.
*
* <p>The {@link #UI_MODE_NIGHT_MASK} defines whether the screen
* is in a special mode. They may be one of {@link #UI_MODE_NIGHT_UNDEFINED},
* {@link #UI_MODE_NIGHT_NO} or {@link #UI_MODE_NIGHT_YES}.
*/
public int uiMode;
/**
* @hide Internal book-keeping.
*/
public int seq;
/**
* 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) {
setTo(o);
}
public void setTo(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;
navigationHidden = o.navigationHidden;
orientation = o.orientation;
screenLayout = o.screenLayout;
uiMode = o.uiMode;
seq = o.seq;
}
public String toString() {
StringBuilder sb = new StringBuilder(128);
sb.append("{ scale=");
sb.append(fontScale);
sb.append(" imsi=");
sb.append(mcc);
sb.append("/");
sb.append(mnc);
sb.append(" loc=");
sb.append(locale);
sb.append(" touch=");
sb.append(touchscreen);
sb.append(" keys=");
sb.append(keyboard);
sb.append("/");
sb.append(keyboardHidden);
sb.append("/");
sb.append(hardKeyboardHidden);
sb.append(" nav=");
sb.append(navigation);
sb.append("/");
sb.append(navigationHidden);
sb.append(" orien=");
sb.append(orientation);
sb.append(" layout=");
sb.append(screenLayout);
sb.append(" uiMode=");
sb.append(uiMode);
if (seq != 0) {
sb.append(" seq=");
sb.append(seq);
}
sb.append('}');
return sb.toString();
}
/**
* Set this object to the system defaults.
*/
public void setToDefaults() {
fontScale = 1;
mcc = mnc = 0;
locale = null;
userSetLocale = false;
touchscreen = TOUCHSCREEN_UNDEFINED;
keyboard = KEYBOARD_UNDEFINED;
keyboardHidden = KEYBOARDHIDDEN_UNDEFINED;
hardKeyboardHidden = HARDKEYBOARDHIDDEN_UNDEFINED;
navigation = NAVIGATION_UNDEFINED;
navigationHidden = NAVIGATIONHIDDEN_UNDEFINED;
orientation = ORIENTATION_UNDEFINED;
screenLayout = SCREENLAYOUT_SIZE_UNDEFINED;
uiMode = UI_MODE_TYPE_UNDEFINED;
seq = 0;
}
/** {@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.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
&& navigationHidden != delta.navigationHidden) {
changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
navigationHidden = delta.navigationHidden;
}
if (delta.orientation != ORIENTATION_UNDEFINED
&& orientation != delta.orientation) {
changed |= ActivityInfo.CONFIG_ORIENTATION;
orientation = delta.orientation;
}
if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
&& screenLayout != delta.screenLayout) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
screenLayout = delta.screenLayout;
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
changed |= ActivityInfo.CONFIG_UI_MODE;
if ((delta.uiMode&UI_MODE_TYPE_MASK) != UI_MODE_TYPE_UNDEFINED) {
uiMode = (uiMode&~UI_MODE_TYPE_MASK)
| (delta.uiMode&UI_MODE_TYPE_MASK);
}
if ((delta.uiMode&UI_MODE_NIGHT_MASK) != UI_MODE_NIGHT_UNDEFINED) {
uiMode = (uiMode&~UI_MODE_NIGHT_MASK)
| (delta.uiMode&UI_MODE_NIGHT_MASK);
}
}
if (delta.seq != 0) {
seq = delta.seq;
}
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},
* {@link android.content.pm.ActivityInfo#CONFIG_ORIENTATION
* PackageManager.ActivityInfo.CONFIG_ORIENTATION}, or
* {@link android.content.pm.ActivityInfo#CONFIG_SCREEN_LAYOUT
* PackageManager.ActivityInfo.CONFIG_SCREEN_LAYOUT}.
*/
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.navigationHidden != NAVIGATIONHIDDEN_UNDEFINED
&& navigationHidden != delta.navigationHidden) {
changed |= ActivityInfo.CONFIG_KEYBOARD_HIDDEN;
}
if (delta.orientation != ORIENTATION_UNDEFINED
&& orientation != delta.orientation) {
changed |= ActivityInfo.CONFIG_ORIENTATION;
}
if (delta.screenLayout != SCREENLAYOUT_SIZE_UNDEFINED
&& screenLayout != delta.screenLayout) {
changed |= ActivityInfo.CONFIG_SCREEN_LAYOUT;
}
if (delta.uiMode != (UI_MODE_TYPE_UNDEFINED|UI_MODE_NIGHT_UNDEFINED)
&& uiMode != delta.uiMode) {
changed |= ActivityInfo.CONFIG_UI_MODE;
}
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;
}
/**
* @hide Return true if the sequence of 'other' is better than this. Assumes
* that 'this' is your current sequence and 'other' is a new one you have
* received some how and want to compare with what you have.
*/
public boolean isOtherSeqNewer(Configuration other) {
if (other == null) {
// Sanity check.
return false;
}
if (other.seq == 0) {
// If the other sequence is not specified, then we must assume
// it is newer since we don't know any better.
return true;
}
if (seq == 0) {
// If this sequence is not specified, then we also consider the
// other is better. Yes we have a preference for other. Sue us.
return true;
}
int diff = other.seq - seq;
if (diff > 0x10000) {
// If there has been a sufficiently large jump, assume the
// sequence has wrapped around.
return false;
}
return diff > 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(navigationHidden);
dest.writeInt(orientation);
dest.writeInt(screenLayout);
dest.writeInt(uiMode);
dest.writeInt(seq);
}
public void readFromParcel(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();
navigationHidden = source.readInt();
orientation = source.readInt();
screenLayout = source.readInt();
uiMode = source.readInt();
seq = source.readInt();
}
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) {
readFromParcel(source);
}
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;
if (this.locale == null) {
if (that.locale != null) return 1;
} else if (that.locale == null) {
return -1;
} else {
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.navigationHidden - that.navigationHidden;
if (n != 0) return n;
n = this.orientation - that.orientation;
if (n != 0) return n;
n = this.screenLayout - that.screenLayout;
if (n != 0) return n;
n = this.uiMode - that.uiMode;
//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 != null ? this.locale.hashCode() : 0)
+ this.touchscreen
+ this.keyboard + this.keyboardHidden + this.hardKeyboardHidden
+ this.navigation + this.navigationHidden
+ this.orientation + this.screenLayout + this.uiMode;
}
}
+19
View File
@@ -0,0 +1,19 @@
/*
* Copyright (C) 2010 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 ObbInfo;
@@ -0,0 +1,106 @@
/*
* Copyright (C) 2010 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.Parcelable;
/**
* Basic information about a Opaque Binary Blob (OBB) that reflects the info
* from the footer on the OBB file. This information may be manipulated by a
* developer with the <code>obbtool</code> program in the Android SDK.
*/
public class ObbInfo implements Parcelable {
/** Flag noting that this OBB is an overlay patch for a base OBB. */
public static final int OBB_OVERLAY = 1 << 0;
/**
* The canonical filename of the OBB.
*/
public String filename;
/**
* The name of the package to which the OBB file belongs.
*/
public String packageName;
/**
* The version of the package to which the OBB file belongs.
*/
public int version;
/**
* The flags relating to the OBB.
*/
public int flags;
/**
* The salt for the encryption algorithm.
*
* @hide
*/
public byte[] salt;
// Only allow things in this package to instantiate.
/* package */ ObbInfo() {
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("ObbInfo{");
sb.append(Integer.toHexString(System.identityHashCode(this)));
sb.append(" packageName=");
sb.append(packageName);
sb.append(",version=");
sb.append(version);
sb.append(",flags=");
sb.append(flags);
sb.append('}');
return sb.toString();
}
public int describeContents() {
return 0;
}
public void writeToParcel(Parcel dest, int parcelableFlags) {
dest.writeString(filename);
dest.writeString(packageName);
dest.writeInt(version);
dest.writeInt(flags);
dest.writeByteArray(salt);
}
public static final Parcelable.Creator<ObbInfo> CREATOR
= new Parcelable.Creator<ObbInfo>() {
public ObbInfo createFromParcel(Parcel source) {
return new ObbInfo(source);
}
public ObbInfo[] newArray(int size) {
return new ObbInfo[size];
}
};
private ObbInfo(Parcel source) {
filename = source.readString();
packageName = source.readString();
version = source.readInt();
flags = source.readInt();
salt = source.createByteArray();
}
}
@@ -0,0 +1,63 @@
/*
* Copyright (C) 2010 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.io.File;
import java.io.IOException;
/**
* Class to scan Opaque Binary Blob (OBB) files. Use this to get information
* about an OBB file for use in a program via {@link ObbInfo}.
*/
public class ObbScanner {
// Don't allow others to instantiate this class
private ObbScanner() {}
/**
* Scan a file for OBB information.
*
* @param filePath path to the OBB file to be scanned.
* @return ObbInfo object information corresponding to the file path
* @throws IllegalArgumentException if the OBB file couldn't be found
* @throws IOException if the OBB file couldn't be read
*/
public static ObbInfo getObbInfo(String filePath) throws IOException {
if (filePath == null) {
throw new IllegalArgumentException("file path cannot be null");
}
final File obbFile = new File(filePath);
if (!obbFile.exists()) {
throw new IllegalArgumentException("OBB file does not exist: " + filePath);
}
/*
* XXX This will fail to find the real canonical path if bind mounts are
* used, but we don't use any bind mounts right now.
*/
final String canonicalFilePath = obbFile.getCanonicalPath();
ObbInfo obbInfo = new ObbInfo();
obbInfo.filename = canonicalFilePath;
getObbInfo_native(canonicalFilePath, obbInfo);
return obbInfo;
}
private native static void getObbInfo_native(String filePath, ObbInfo obbInfo)
throws IOException;
}
@@ -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;
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,408 @@
/*
* 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, true),
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.WithDensity {
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) {
// Should not get called, at least not by StaticLayout.
chooseHeight(text, start, end, spanstartv, v, fm, null);
}
public void chooseHeight(CharSequence text, int start, int end,
int spanstartv, int v,
Paint.FontMetricsInt fm, TextPaint paint) {
int size = mSize;
if (paint != null) {
size *= paint.density;
}
if (fm.bottom - fm.top < size) {
fm.top = fm.bottom - size;
fm.ascent = fm.ascent - size;
} 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 (size - fm.descent >= need) {
/*
* It is safe to shrink the ascent this much.
*/
fm.top = fm.bottom - size;
fm.ascent = fm.descent - size;
} else if (size >= 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 + size;
} else {
/*
* Show as much of the ascent as we can, and no descent.
*/
fm.top = fm.ascent = -size;
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);
}
@@ -0,0 +1,740 @@
/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.content.res;
import 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;
}
/**
* @hide
* Retrieve the string value for the attribute at <var>index</var> that is
* not allowed to change with the given configurations.
*
* @param index Index of attribute to retrieve.
* @param allowedChangingConfigs Bit mask of configurations from
* ActivityInfo that are allowed to change.
*
* @return String holding string data. Any styling information is
* removed. Returns null if the attribute is not defined.
*/
public String getNonConfigurationString(int index, int allowedChangingConfigs) {
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if ((data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS]&~allowedChangingConfigs) != 0) {
return null;
}
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 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];
outValue.density = data[index+AssetManager.STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
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);
}
}
@@ -0,0 +1,516 @@
/*
* 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(hashCode());
}
}
}
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);
}
@@ -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();
}
@@ -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>