Added current version.

This commit is contained in:
Ryszard Wiśniewski 2010-03-12 12:47:56 +01:00
commit 48e1afd6b1
55 changed files with 4671 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
/nbproject
/build
/dist
/build.xml

View File

@ -0,0 +1,74 @@
/*
* 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 brut.util.Jar;
/**
* Conveniences for retrieving data out of a compiled string resource.
*
* {@hide}
*/
final class StringBlock {
static {
Jar.load("/libAndroid.so");
}
private final int mNative;
private final boolean mOwnsNative;
private CharSequence[] mStrings;
public CharSequence get(int idx) {
synchronized (this) {
if (mStrings != null) {
CharSequence res = mStrings[idx];
if (res != null) {
return res;
}
} else {
final int num = nativeGetSize(mNative);
mStrings = new CharSequence[num];
}
String str = nativeGetString(mNative, idx);
CharSequence res = str;
mStrings[idx] = res;
return res;
}
}
protected void finalize() throws Throwable {
if (mOwnsNative) {
nativeDestroy(mNative);
}
}
/**
* 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;
mOwnsNative = false;
}
private static final native int nativeGetSize(int obj);
private static final native String nativeGetString(int obj, int idx);
private static final native void nativeDestroy(int obj);
}

View File

@ -0,0 +1,364 @@
/*
* 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 brut.util.Jar;
import org.xmlpull.v1.XmlPullParser;
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 public class XmlBlock {
static {
Jar.load("/libAndroid.so");
}
private static final boolean DEBUG=false;
public XmlBlock(byte[] data) {
mNative = nativeCreate(data, 0, data.length);
mStrings = new StringBlock(nativeGetStringBlock(mNative), false);
}
public XmlBlock(byte[] data, int offset, int size) {
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);
}
}
public XmlPullParser newParser() {
synchronized (this) {
if (mNative != 0) {
return new Parser(nativeCreateParseState(mNative), this);
}
return null;
}
}
/*package*/ final class Parser implements XmlPullParser {
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 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();
}
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 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 nativeGetAttributeIndex(int state, String namespace, String name);
private static final native void nativeDestroyParseState(int state);
private static final native void nativeDestroy(int obj);
}

View File

@ -0,0 +1,234 @@
/*
* 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.util;
/**
* Container for a dynamically typed data value. Primarily used with
* {@link android.content.res.Resources} for holding resource values.
*/
public class TypedValue {
/** The value contains no data. */
public static final int TYPE_NULL = 0x00;
/** The <var>data</var> field holds a resource identifier. */
public static final int TYPE_REFERENCE = 0x01;
/** The <var>data</var> field holds an attribute resource
* identifier (referencing an attribute in the current theme
* style, not a resource entry). */
public static final int TYPE_ATTRIBUTE = 0x02;
/** The <var>string</var> field holds string data. In addition, if
* <var>data</var> is non-zero then it is the string block
* index of the string and <var>assetCookie</var> is the set of
* assets the string came from. */
public static final int TYPE_STRING = 0x03;
/** The <var>data</var> field holds an IEEE 754 floating point number. */
public static final int TYPE_FLOAT = 0x04;
/** The <var>data</var> field holds a complex number encoding a
* dimension value. */
public static final int TYPE_DIMENSION = 0x05;
/** The <var>data</var> field holds a complex number encoding a fraction
* of a container. */
public static final int TYPE_FRACTION = 0x06;
/** Identifies the start of plain integer values. Any type value
* from this to {@link #TYPE_LAST_INT} means the
* <var>data</var> field holds a generic integer value. */
public static final int TYPE_FIRST_INT = 0x10;
/** The <var>data</var> field holds a number that was
* originally specified in decimal. */
public static final int TYPE_INT_DEC = 0x10;
/** The <var>data</var> field holds a number that was
* originally specified in hexadecimal (0xn). */
public static final int TYPE_INT_HEX = 0x11;
/** The <var>data</var> field holds 0 or 1 that was originally
* specified as "false" or "true". */
public static final int TYPE_INT_BOOLEAN = 0x12;
/** Identifies the start of integer values that were specified as
* color constants (starting with '#'). */
public static final int TYPE_FIRST_COLOR_INT = 0x1c;
/** The <var>data</var> field holds a color that was originally
* specified as #aarrggbb. */
public static final int TYPE_INT_COLOR_ARGB8 = 0x1c;
/** The <var>data</var> field holds a color that was originally
* specified as #rrggbb. */
public static final int TYPE_INT_COLOR_RGB8 = 0x1d;
/** The <var>data</var> field holds a color that was originally
* specified as #argb. */
public static final int TYPE_INT_COLOR_ARGB4 = 0x1e;
/** The <var>data</var> field holds a color that was originally
* specified as #rgb. */
public static final int TYPE_INT_COLOR_RGB4 = 0x1f;
/** Identifies the end of integer values that were specified as color
* constants. */
public static final int TYPE_LAST_COLOR_INT = 0x1f;
/** Identifies the end of plain integer values. */
public static final int TYPE_LAST_INT = 0x1f;
/* ------------------------------------------------------------ */
/** Complex data: bit location of unit information. */
public static final int COMPLEX_UNIT_SHIFT = 0;
/** Complex data: mask to extract unit information (after shifting by
* {@link #COMPLEX_UNIT_SHIFT}). This gives us 16 possible types, as
* defined below. */
public static final int COMPLEX_UNIT_MASK = 0xf;
/** {@link #TYPE_DIMENSION} complex unit: Value is raw pixels. */
public static final int COMPLEX_UNIT_PX = 0;
/** {@link #TYPE_DIMENSION} complex unit: Value is Device Independent
* Pixels. */
public static final int COMPLEX_UNIT_DIP = 1;
/** {@link #TYPE_DIMENSION} complex unit: Value is a scaled pixel. */
public static final int COMPLEX_UNIT_SP = 2;
/** {@link #TYPE_DIMENSION} complex unit: Value is in points. */
public static final int COMPLEX_UNIT_PT = 3;
/** {@link #TYPE_DIMENSION} complex unit: Value is in inches. */
public static final int COMPLEX_UNIT_IN = 4;
/** {@link #TYPE_DIMENSION} complex unit: Value is in millimeters. */
public static final int COMPLEX_UNIT_MM = 5;
/** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall
* size. */
public static final int COMPLEX_UNIT_FRACTION = 0;
/** {@link #TYPE_FRACTION} complex unit: A fraction of the parent size. */
public static final int COMPLEX_UNIT_FRACTION_PARENT = 1;
/** Complex data: where the radix information is, telling where the decimal
* place appears in the mantissa. */
public static final int COMPLEX_RADIX_SHIFT = 4;
/** Complex data: mask to extract radix information (after shifting by
* {@link #COMPLEX_RADIX_SHIFT}). This give us 4 possible fixed point
* representations as defined below. */
public static final int COMPLEX_RADIX_MASK = 0x3;
/** Complex data: the mantissa is an integral number -- i.e., 0xnnnnnn.0 */
public static final int COMPLEX_RADIX_23p0 = 0;
/** Complex data: the mantissa magnitude is 16 bits -- i.e, 0xnnnn.nn */
public static final int COMPLEX_RADIX_16p7 = 1;
/** Complex data: the mantissa magnitude is 8 bits -- i.e, 0xnn.nnnn */
public static final int COMPLEX_RADIX_8p15 = 2;
/** Complex data: the mantissa magnitude is 0 bits -- i.e, 0x0.nnnnnn */
public static final int COMPLEX_RADIX_0p23 = 3;
/** Complex data: bit location of mantissa information. */
public static final int COMPLEX_MANTISSA_SHIFT = 8;
/** Complex data: mask to extract mantissa information (after shifting by
* {@link #COMPLEX_MANTISSA_SHIFT}). This gives us 23 bits of precision;
* the top bit is the sign. */
public static final int COMPLEX_MANTISSA_MASK = 0xffffff;
/* ------------------------------------------------------------ */
/**
* If {@link #density} is equal to this value, then the density should be
* treated as the system's default density value: {@link DisplayMetrics#DENSITY_DEFAULT}.
*/
public static final int DENSITY_DEFAULT = 0;
/**
* If {@link #density} is equal to this value, then there is no density
* associated with the resource and it should not be scaled.
*/
public static final int DENSITY_NONE = 0xffff;
/* ------------------------------------------------------------ */
/** The type held by this value, as defined by the constants here.
* This tells you how to interpret the other fields in the object. */
public int type;
private static final float MANTISSA_MULT =
1.0f / (1<<TypedValue.COMPLEX_MANTISSA_SHIFT);
private static final float[] RADIX_MULTS = new float[] {
1.0f*MANTISSA_MULT, 1.0f/(1<<7)*MANTISSA_MULT,
1.0f/(1<<15)*MANTISSA_MULT, 1.0f/(1<<23)*MANTISSA_MULT
};
/**
* Retrieve the base value from a complex data integer. This uses the
* {@link #COMPLEX_MANTISSA_MASK} and {@link #COMPLEX_RADIX_MASK} fields of
* the data to compute a floating point representation of the number they
* describe. The units are ignored.
*
* @param complex A complex data value.
*
* @return A floating point value corresponding to the complex data.
*/
public static float complexToFloat(int complex)
{
return (complex&(TypedValue.COMPLEX_MANTISSA_MASK
<<TypedValue.COMPLEX_MANTISSA_SHIFT))
* RADIX_MULTS[(complex>>TypedValue.COMPLEX_RADIX_SHIFT)
& TypedValue.COMPLEX_RADIX_MASK];
}
private static final String[] DIMENSION_UNIT_STRS = new String[] {
"px", "dip", "sp", "pt", "in", "mm"
};
private static final String[] FRACTION_UNIT_STRS = new String[] {
"%", "%p"
};
/**
* Perform type conversion as per {@link #coerceToString()} on an
* explicitly supplied type and data.
*
* @param type The data type identifier.
* @param data The data value.
*
* @return String The coerced string value. If the value is
* null or the type is not known, null is returned.
*/
public static final String coerceToString(int type, int data)
{
switch (type) {
case TYPE_NULL:
return null;
case TYPE_REFERENCE:
return "@" + data;
case TYPE_ATTRIBUTE:
return "?" + data;
case TYPE_FLOAT:
return Float.toString(Float.intBitsToFloat(data));
case TYPE_DIMENSION:
return Float.toString(complexToFloat(data)) + DIMENSION_UNIT_STRS[
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
case TYPE_FRACTION:
return Float.toString(complexToFloat(data)*100) + FRACTION_UNIT_STRS[
(data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK];
case TYPE_INT_HEX:
return "0x" + Integer.toHexString(data);
case TYPE_INT_BOOLEAN:
return data != 0 ? "true" : "false";
}
if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
return "#" + Integer.toHexString(data);
} else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
return Integer.toString(data);
}
return null;
}
};

View File

@ -0,0 +1,152 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResTable;
import brut.common.BrutException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import brut.directory.Util;
import brut.directory.ZipRODirectory;
import brut.util.OS;
import java.io.File;
import java.io.IOException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class Androlib {
private final String mAndroidJar;
private final AndrolibResources mAndRes;
private final AndrolibSmali mSmali;
public Androlib(String androidJar) {
mAndroidJar = androidJar;
mAndRes = new AndrolibResources(mAndroidJar);
mSmali = new AndrolibSmali();
}
public void decode(String apkFileName, String outDirName)
throws AndrolibException {
decode(new File(apkFileName), new File(outDirName));
}
public void decode(File apkFile, File outDir)
throws AndrolibException {
try {
OS.rmdir(outDir);
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
outDir.mkdirs();
ResTable resTable = mAndRes.getResTable(apkFile);
mAndRes.decode(resTable, apkFile, outDir);
File smaliDir = new File(outDir.getPath() + "/smali");
mSmali.baksmali(apkFile, smaliDir);
mAndRes.tagSmaliResIDs(resTable, smaliDir);
try {
Directory in = new ZipRODirectory(apkFile);
Directory out = new FileDirectory(outDir);
if (in.containsDir("assets")) {
Util.copyFiles(in.getDir("assets"), out.createDir("assets"));
}
if (in.containsDir("lib")) {
Util.copyFiles(in.getDir("lib"), out.createDir("lib"));
}
} catch (DirectoryException ex) {
throw new AndrolibException("Could not decode apk", ex);
}
}
public void buildAll() throws AndrolibException {
clean();
new File("build/apk").mkdirs();
buildCopyRawFiles();
buildResources();
buildClasses();
buildPackage();
}
public void clean() throws AndrolibException {
try {
OS.rmdir("build");
OS.rmdir("dist");
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}
public void buildCopyRawFiles() throws AndrolibException {
try {
if (new File("assets").exists()) {
OS.cpdir("assets", "build/apk/assets");
}
if (new File("lib").exists()) {
OS.cpdir("lib", "build/apk/lib");
}
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}
public void buildResources() throws AndrolibException {
File apkFile;
try {
apkFile = File.createTempFile("APKTOOL", null);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
apkFile.delete();
mAndRes.aaptPackage(
apkFile,
new File("AndroidManifest.xml"),
new File("res")
);
mAndRes.updateSmaliResIDs(
mAndRes.getResTable(apkFile), new File("smali"));
try {
Util.copyFiles(new ZipRODirectory(apkFile),
new FileDirectory("build/apk"));
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
apkFile.delete();
}
public void buildClasses() throws AndrolibException {
new AndrolibSmali().smali("smali", "build/apk/classes.dex");
}
public void buildPackage() throws AndrolibException {
File distDir = new File("dist");
if (! distDir.exists()) {
distDir.mkdirs();
}
mAndRes.aaptPackage(new File("dist/out.apk"), null, null,
new File("build/apk"), false);
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib;
import brut.common.BrutException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class AndrolibException extends BrutException {
public AndrolibException() {
}
public AndrolibException(String message) {
super(message);
}
public AndrolibException(String message, Throwable cause) {
super(message, cause);
}
public AndrolibException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib;
import java.io.File;
import java.io.IOException;
import org.jf.baksmali.baksmali;
import org.jf.dexlib.DexFile;
import org.jf.smali.main;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
class AndrolibSmali {
public void baksmali(File apkFile, File dir) throws AndrolibException {
baksmali(apkFile.getAbsolutePath(), dir.getAbsolutePath());
}
public void baksmali(String apkFile, String dir) throws AndrolibException {
try {
DexFile dexFile = new DexFile(apkFile);
baksmali.disassembleDexFile(dexFile, false, dir, new String[]{}, "", false, true, true, true, false, 0, false);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
public void smali(File dir, File dexFile) throws AndrolibException {
smali(dir.getAbsolutePath(), dexFile.getAbsolutePath());
}
public void smali(String dir, String dexFile) throws AndrolibException {
main.main(new String[]{"smali", dir, "-o", dexFile});
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResTable;
import brut.common.BrutException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import brut.directory.ZipRODirectory;
import brut.util.OS;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ApkFile {
private final AndrolibResources mAndRes;
private final File mApkFile;
private final AndrolibSmali mSmali;
private ResTable mResTable;
public ApkFile(AndrolibResources andRes, String apkFileName) {
this(andRes, new File(apkFileName));
}
public ApkFile(AndrolibResources andRes, File apkFile) {
mAndRes = andRes;
mApkFile = apkFile;
mSmali = new AndrolibSmali();
}
public void decode(String outDirName) throws AndrolibException {
decode(new File(outDirName));
}
public void decode(File outDir) throws AndrolibException {
if (outDir.exists()) {
throw new AndrolibException("Output directory already exists: " +
outDir.getAbsolutePath());
}
outDir.mkdirs();
File smaliDir = new File(outDir.getPath() + "/smali");
mAndRes.decode(getResTable(), mApkFile, outDir);
mSmali.baksmali(mApkFile, smaliDir);
mAndRes.tagSmaliResIDs(getResTable(), smaliDir);
try {
Directory in = new ZipRODirectory(mApkFile);
Directory out = new FileDirectory(outDir);
if (in.containsDir("assets")) {
Directory in2 = in.getDir("assets");
Directory out2 = out.createDir("assets");
for (String fileName : in2.getFiles(true)) {
IOUtils.copy(in2.getFileInput(fileName),
out2.getFileOutput(fileName));
}
}
if (in.containsDir("lib")) {
Directory in2 = in.getDir("lib");
Directory out2 = out.createDir("lib");
for (String fileName : in2.getFiles(true)) {
IOUtils.copy(in2.getFileInput(fileName),
out2.getFileOutput(fileName));
}
}
} catch (DirectoryException ex) {
throw new AndrolibException("Could not decode apk", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode apk", ex);
}
}
public void build(String inDir) throws AndrolibException {
build(new File(inDir));
}
public void build(File inDir) throws AndrolibException {
try {
File smaliDir = new File(inDir.getPath() + "/smali");
mAndRes.aaptPackage(
mApkFile,
new File(inDir.getPath() + "/AndroidManifest.xml"),
new File(inDir.getPath() + "/res")
);
mAndRes.updateSmaliResIDs(getResTable(), smaliDir);
File tmpDir = OS.createTempDirectory();
try {
mSmali.smali(smaliDir,
new File(tmpDir.getPath() + "/classes.dex"));
if (new File(inDir.getPath() + "/assets").exists()) {
OS.cpdir(inDir.getPath() + "/assets",
tmpDir.getPath() + "/assets");
}
if (new File(inDir.getPath() + "/lib").exists()) {
OS.cpdir(inDir.getPath() + "/lib",
tmpDir.getPath() + "/lib");
}
mAndRes.aaptPackage(
mApkFile,
null,
null,
tmpDir,
true
);
} finally {
OS.rmdir(tmpDir);
}
} catch (BrutException ex) {
throw new AndrolibException(
"Could not build apk for dir: " + inDir.getAbsolutePath(), ex);
}
}
public ResTable getResTable() throws AndrolibException {
if (mResTable == null) {
mResTable = mAndRes.getResTable(mApkFile);
}
return mResTable;
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res;
import brut.androlib.*;
import brut.androlib.res.data.*;
import brut.androlib.res.data.value.ResFileValue;
import brut.androlib.res.data.value.ResXmlSerializable;
import brut.androlib.res.decoder.*;
import brut.androlib.res.jni.JniPackage;
import brut.androlib.res.jni.JniPackageGroup;
import brut.common.BrutException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import brut.directory.ZipRODirectory;
import brut.util.Jar;
import brut.util.OS;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
final public class AndrolibResources {
static {
Jar.load("/libAndroid.so");
}
private final File mAndroidJar;
public AndrolibResources(String androidJar) {
this(new File(androidJar));
}
public AndrolibResources(File androidJar) {
this.mAndroidJar = androidJar;
}
public ResTable getResTable(File apkFile) throws AndrolibException {
ResTable resTable = new ResTable();
loadApk(resTable, new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()), false);
loadApk(resTable, apkFile, true);
return resTable;
}
public void decode(ResTable resTable, File apkFile, File outDir)
throws AndrolibException {
ResXmlSerializer serial = getResXmlSerializer(resTable);
ResFileDecoder fileDecoder = getResFileDecoder(serial);
serial.setCurrentPackage(
resTable.listMainPackages().iterator().next());
Directory in, out;
try {
in = new ZipRODirectory(apkFile);
out = new FileDirectory(outDir);
fileDecoder.decode(
in, "AndroidManifest.xml", out, "AndroidManifest.xml", "xml");
in = in.getDir("res");
out = out.createDir("res");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
for (ResPackage pkg : resTable.listMainPackages()) {
serial.setCurrentPackage(pkg);
for (ResResource res : pkg.listFiles()) {
ResFileValue fileValue = (ResFileValue) res.getValue();
fileDecoder.decode(in, fileValue.getStrippedPath(),
out, res.getFilePath());
}
for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
generateValuesFile(valuesFile, out, serial);
}
}
}
public void aaptPackage(File apkFile, File manifest, File resDir)
throws AndrolibException {
aaptPackage(apkFile, manifest, resDir, null, false);
}
public void aaptPackage(File apkFile, File manifest, File resDir,
File rawDir, boolean update) throws AndrolibException {
String[] cmd = new String[12];
int i = 0;
cmd[i++] = "aapt";
cmd[i++] = "p";
if (update) {
cmd[i++] = "-u";
}
cmd[i++] = "-F";
cmd[i++] = apkFile.getAbsolutePath();
cmd[i++] = "-I";
cmd[i++] = mAndroidJar.getAbsolutePath();
if (manifest != null) {
cmd[i++] = "-M";
cmd[i++] = manifest.getAbsolutePath();
}
if (resDir != null) {
cmd[i++] = "-S";
cmd[i++] = resDir.getAbsolutePath();
}
if (rawDir != null) {
cmd[i++] = rawDir.getAbsolutePath();
}
try {
OS.exec(Arrays.copyOf(cmd, i));
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}
public void tagSmaliResIDs(ResTable resTable, File smaliDir)
throws AndrolibException {
new ResSmaliUpdater().tagResIDs(resTable, smaliDir);
}
public void updateSmaliResIDs(ResTable resTable, File smaliDir) throws AndrolibException {
new ResSmaliUpdater().updateResIDs(resTable, smaliDir);
}
public ResFileDecoder getResFileDecoder(ResXmlSerializer serializer) {
ResStreamDecoderContainer decoders =
new ResStreamDecoderContainer();
decoders.setDecoder("raw", new ResRawStreamDecoder());
decoders.setDecoder("xml",
new ResXmlStreamDecoder(serializer));
return new ResFileDecoder(decoders);
}
public ResXmlSerializer getResXmlSerializer(ResTable resTable) {
ResXmlSerializer serial = new ResXmlSerializer();
serial.setProperty(serial.PROPERTY_SERIALIZER_INDENTATION, " ");
return serial;
}
private void generateValuesFile(ResValuesFile valuesFile, Directory out,
ResXmlSerializer serial) throws AndrolibException {
try {
OutputStream outStream = out.getFileOutput(valuesFile.getPath());
serial.setOutput((outStream), null);
serial.setDecodingEnabled(false);
serial.startDocument(null, null);
serial.startTag(null, "resources");
for (ResResource res : valuesFile.listResources()) {
((ResXmlSerializable) res.getValue())
.serializeToXml(serial, res);
}
serial.endTag(null, "resources");
serial.endDocument();
serial.flush();
outStream.close();
} catch (IOException ex) {
throw new AndrolibException(
"Could not generate: " + valuesFile.getPath(), ex);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not generate: " + valuesFile.getPath(), ex);
}
}
private void loadApk(ResTable resTable, File apkFile, boolean main)
throws AndrolibException {
JniPackageGroup[] groups =
nativeGetPackageGroups(apkFile.getAbsolutePath());
if (groups.length != 1) {
throw new AndrolibException(
"Apk's with multiple or zero package groups not supported");
}
for (int i = 0; i < groups.length; i++) {
// if (groups.length != 1 && i == 0) {
// continue;
// }
for (JniPackage jniPkg : groups[i].packages) {
ResPackage pkg = new JniPackageDecoder().decode(jniPkg, resTable);
resTable.addPackage(pkg, main);
}
}
}
public static String escapeForResXml(String value) {
value = value.replace("'", "\\'");
value = value.replace("\n", "\\n\n");
char c = value.charAt(0);
if (c == '@' || c == '#' || c == '?') {
return '\\' + value;
}
return value;
}
private static final native JniPackageGroup[] nativeGetPackageGroups(
String apkFileName);
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResSpec;
import brut.androlib.res.data.ResTable;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResSmaliUpdater {
public void tagResIDs(ResTable resTable, File smaliDir)
throws AndrolibException {
Directory dir = null;
try {
dir = new FileDirectory(smaliDir);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not tag res IDs", ex);
}
for (String fileName : dir.getFiles(true)) {
try {
Iterator<String> it =
IOUtils.readLines(dir.getFileInput(fileName)).iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) {
String line = it.next();
if (RES_NAME_PATTERN.matcher(line).matches()) {
out.println(line);
out.println(it.next());
continue;
}
Matcher m = RES_ID_PATTERN.matcher(line);
if (m.matches()) {
int resID = parseResID(m.group(3));
if (resID != -1) {
ResResSpec spec = resTable.getResSpec(resID);
out.println(String.format(
RES_NAME_FORMAT, spec.getFullName()));
}
}
out.println(line);
}
out.close();
} catch (IOException ex) {
throw new AndrolibException(
"Could not tag resIDs for file: " + fileName, ex);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not tag resIDs for file: " + fileName, ex);
} catch (AndrolibException ex) {
throw new AndrolibException(
"Could not tag resIDs for file: " + fileName, ex);
}
}
}
public void updateResIDs(ResTable resTable, File smaliDir)
throws AndrolibException {
try {
Directory dir = new FileDirectory(smaliDir);
for (String fileName : dir.getFiles(true)) {
Iterator<String> it =
IOUtils.readLines(dir.getFileInput(fileName)).iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) {
String line = it.next();
out.println(line);
Matcher m1 = RES_NAME_PATTERN.matcher(line);
if (! m1.matches()) {
continue;
}
Matcher m2 = RES_ID_PATTERN.matcher(it.next());
if (! m2.matches()) {
throw new AndrolibException();
}
int resID = resTable.getPackage(m1.group(1))
.getType(m1.group(2)).getResSpec(m1.group(3))
.getId().id;
if (m2.group(1) != null) {
out.println(String.format(
RES_ID_FORMAT_FIELD, m2.group(1), resID));
} else {
out.println(String.format(
RES_ID_FORMAT_CONST, m2.group(2), resID));
}
}
out.close();
}
} catch (IOException ex) {
throw new AndrolibException(
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex);
}
}
private int parseResID(String resIDHex) {
if (resIDHex.endsWith("ff")) {
return -1;
}
int resID = Integer.valueOf(resIDHex, 16);
if (resIDHex.length() == 4) {
resID = resID << 16;
}
return resID;
}
private final static String RES_ID_FORMAT_FIELD =
".field %s:I = 0x%08x";
private final static String RES_ID_FORMAT_CONST =
" const %s, 0x%08x";
private final static Pattern RES_ID_PATTERN = Pattern.compile(
"^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$");
private final static String RES_NAME_FORMAT =
"# APKTOOL/RES_NAME: %s";
private final static Pattern RES_NAME_PATTERN = Pattern.compile(
"^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$");
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import java.util.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResConfig {
private final ResConfigFlags mFlags;
private final Map<ResResSpec, ResResource> mResources =
new LinkedHashMap<ResResSpec, ResResource>();
public ResConfig(ResConfigFlags flags) {
this.mFlags = flags;
}
public Set<ResResource> listResources() {
return new LinkedHashSet<ResResource>(mResources.values());
}
public ResResource getResource(ResResSpec spec) throws AndrolibException {
ResResource res = mResources.get(spec);
if (res == null) {
throw new AndrolibException(String.format(
"Undefined resource: spec=%s, config=%s", spec, this));
}
return res;
}
public Set<ResResSpec> listResSpecs() {
return mResources.keySet();
}
public ResConfigFlags getFlags() {
return mFlags;
}
public void addResource(ResResource res)
throws AndrolibException {
ResResSpec spec = res.getResSpec();
if (mResources.put(spec, res) != null) {
throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", spec, this));
}
}
@Override
public String toString() {
return mFlags.toString();
}
}

View File

@ -0,0 +1,342 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.jni.JniConfig;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResConfigFlags {
public final int mcc;
public final int mnc;
public final char[] language;
public final char[] country;
// public final Orientation orientation;
// public final Touchscreen touchscreen;
public final int orientation;
public final int touchscreen;
public final int density;
// public final Keyboard keyboard;
// public final Navigation navigation;
// public final Keys keys;
// public final Nav nav;
public final int keyboard;
public final int navigation;
public final int inputFlags;
public final int screenWidth;
public final int screenHeight;
// public final ScreenSize screenSize;
// public final ScreenLong screenLong;
public final int screenLayout;
public final int sdkVersion;
private final String mQualifiers;
public ResConfigFlags() {
mcc = 0;
mnc = 0;
language = new char[]{'\00', '\00'};
country = new char[]{'\00', '\00'};
orientation = ORIENTATION_ANY;
touchscreen = TOUCHSCREEN_ANY;
density = DENSITY_DEFAULT;
keyboard = KEYBOARD_ANY;
navigation = NAVIGATION_ANY;
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
screenWidth = 0;
screenHeight = 0;
screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
sdkVersion = 0;
mQualifiers = "";
}
public ResConfigFlags(JniConfig cfg) throws AndrolibException {
// if (cfg.mcc == 0 && mnc != 0) {
// throw new AndrolibException(String.format(
// "Invalid IMSI: mcc=%3d, mnc=%3d", mcc, mnc));
// }
mcc = cfg.mcc;
mnc = cfg.mnc;
// if (cfg.language.length == 0 && cfg.country.length != 0) {
// throw new AndrolibException(String.format(
// "Invalid local: language=%s, country=%s",
// cfg.language, cfg.country));
// }
language = cfg.language;
country = cfg.country;
//
// switch (cfg.orientation) {
// case ORIENTATION_ANY:
// orientation = Orientation.ANY;
// break;
// case ORIENTATION_LAND:
// orientation = Orientation.LAND;
// break;
// case ORIENTATION_PORT:
// orientation = Orientation.PORT;
// break;
// case ORIENTATION_SQUARE:
// orientation = Orientation.SQUARE;
// break;
// default:
// throw new AndrolibException(String.format(
// "Invalid orientation: %d", cfg.orientation));
// }
orientation = cfg.orientation;
touchscreen = cfg.touchscreen;
density = cfg.density;
keyboard = cfg.keyboard;
navigation = cfg.navigation;
inputFlags = cfg.inputFlags;
screenWidth = cfg.screenWidth;
screenHeight = cfg.screenHeight;
screenLayout = cfg.screenLayout;
sdkVersion = cfg.sdkVersion;
mQualifiers = generateQualifiers();
}
public String getQualifiers() {
return mQualifiers;
}
private String generateQualifiers() {
StringBuilder ret = new StringBuilder();
if (mcc != 0) {
ret.append("-mcc").append(mcc);
if (mnc != 0) {
ret.append("-mnc").append(mnc);
}
}
if (language[0] != '\00') {
ret.append('-').append(language);
if (country[0] != '\00') {
ret.append("-r").append(country);
}
}
switch (screenLayout & MASK_SCREENSIZE) {
case SCREENSIZE_SMALL:
ret.append("-small");
break;
case SCREENSIZE_NORMAL:
ret.append("-normal");
break;
case SCREENSIZE_LARGE:
ret.append("-large");
break;
}
switch (screenLayout & MASK_SCREENLONG) {
case SCREENLONG_YES:
ret.append("-long");
break;
case SCREENLONG_NO:
ret.append("-notlong");
break;
}
switch (orientation) {
case ORIENTATION_PORT:
ret.append("-port");
break;
case ORIENTATION_LAND:
ret.append("-land");
break;
case ORIENTATION_SQUARE:
ret.append("-square");
break;
}
switch (density) {
case DENSITY_DEFAULT:
break;
case DENSITY_LOW:
ret.append("-ldpi");
break;
case DENSITY_MEDIUM:
ret.append("-mdpi");
break;
case DENSITY_HIGH:
ret.append("-hdpi");
break;
case DENSITY_NONE:
ret.append("-nodpi");
break;
default:
ret.append('-').append(density).append("dpi");
}
switch (touchscreen) {
case TOUCHSCREEN_NOTOUCH:
ret.append("-notouch");
break;
case TOUCHSCREEN_STYLUS:
ret.append("-stylus");
break;
case TOUCHSCREEN_FINGER:
ret.append("-finger");
break;
}
switch (inputFlags & MASK_KEYSHIDDEN) {
case KEYSHIDDEN_NO:
ret.append("-keysexposed");
break;
case KEYSHIDDEN_YES:
ret.append("-keyshidden");
break;
case KEYSHIDDEN_SOFT:
ret.append("-keyssoft");
break;
}
switch (keyboard) {
case KEYBOARD_NOKEYS:
ret.append("-nokeys");
break;
case KEYBOARD_QWERTY:
ret.append("-qwerty");
break;
case KEYBOARD_12KEY:
ret.append("-12key");
break;
}
switch (inputFlags & MASK_NAVHIDDEN) {
case NAVHIDDEN_NO:
ret.append("-navexposed");
break;
case NAVHIDDEN_YES:
ret.append("-navhidden");
break;
}
switch (navigation) {
case NAVIGATION_NONAV:
ret.append("-nonav");
break;
case NAVIGATION_DPAD:
ret.append("-dpad");
break;
case NAVIGATION_TRACKBALL:
ret.append("-trackball");
break;
case NAVIGATION_WHEEL:
ret.append("-wheel");
break;
}
if (screenWidth != 0 && screenHeight != 0) {
if (screenWidth > screenHeight) {
ret.append(String.format("-%dx%d", screenWidth, screenHeight));
} else {
ret.append(String.format("-%dx%d", screenHeight, screenWidth));
}
}
if (sdkVersion != 0) {
ret.append("-v").append(sdkVersion);
}
return ret.toString();
}
@Override
public String toString() {
return ! getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]";
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResConfigFlags other = (ResConfigFlags) obj;
if ((this.mQualifiers == null) ? (other.mQualifiers != null) : !this.mQualifiers.equals(other.mQualifiers)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + (this.mQualifiers != null ? this.mQualifiers.hashCode() : 0);
return hash;
}
// public enum Orientation {ANY, PORT, LAND, SQUARE}
// public enum Touchscreen {ANY, NOTOUCH, STYLUS, FINGER}
// public enum Keyboard {ANY, NOKEYS, QWERTY, KEY12}
// public enum Navigation {ANY, NONAV, DPAD, TRACKBALL, WHEEL}
// public enum Keys {ANY, EXPOSED, HIDDEN, SOFT}
// public enum Nav {ANY, EXPOSED, HIDDEN}
// public enum ScreenSize {ANY, SMALL, NORMAL, LARGE}
// public enum ScreenLong {ANY, LONG, NOTLONG}
public final static int ORIENTATION_ANY = 0x0000;
public final static int ORIENTATION_PORT = 0x0001;
public final static int ORIENTATION_LAND = 0x0002;
public final static int ORIENTATION_SQUARE = 0x0003;
public final static int TOUCHSCREEN_ANY = 0x0000;
public final static int TOUCHSCREEN_NOTOUCH = 0x0001;
public final static int TOUCHSCREEN_STYLUS = 0x0002;
public final static int TOUCHSCREEN_FINGER = 0x0003;
public final static int DENSITY_DEFAULT = 0;
public final static int DENSITY_LOW = 120;
public final static int DENSITY_MEDIUM = 160;
public final static int DENSITY_HIGH = 240;
public final static int DENSITY_NONE = 0xffff;
public final static int KEYBOARD_ANY = 0x0000;
public final static int KEYBOARD_NOKEYS = 0x0001;
public final static int KEYBOARD_QWERTY = 0x0002;
public final static int KEYBOARD_12KEY = 0x0003;
public final static int NAVIGATION_ANY = 0x0000;
public final static int NAVIGATION_NONAV = 0x0001;
public final static int NAVIGATION_DPAD = 0x0002;
public final static int NAVIGATION_TRACKBALL = 0x0003;
public final static int NAVIGATION_WHEEL = 0x0004;
public final static int MASK_KEYSHIDDEN = 0x0003;
public final static int KEYSHIDDEN_ANY = 0x0000;
public final static int KEYSHIDDEN_NO = 0x0001;
public final static int KEYSHIDDEN_YES = 0x0002;
public final static int KEYSHIDDEN_SOFT = 0x0003;
public final static int MASK_NAVHIDDEN = 0x000c;
public final static int NAVHIDDEN_ANY = 0x0000;
public final static int NAVHIDDEN_NO = 0x0004;
public final static int NAVHIDDEN_YES = 0x0008;
public final static int MASK_SCREENSIZE = 0x0f;
public final static int SCREENSIZE_ANY = 0x00;
public final static int SCREENSIZE_SMALL = 0x01;
public final static int SCREENSIZE_NORMAL = 0x02;
public final static int SCREENSIZE_LARGE = 0x03;
public final static int MASK_SCREENLONG = 0x30;
public final static int SCREENLONG_ANY = 0x00;
public final static int SCREENLONG_NO = 0x10;
public final static int SCREENLONG_YES = 0x20;
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResID {
public final int package_;
public final int type;
public final int entry;
public final int id;
public ResID(int package_, int type, int entry) {
this(package_, type, entry, (package_ << 24) + (type << 16) + entry);
}
public ResID(int id) {
this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id);
}
public ResID(int package_, int type, int entry, int id) {
this.package_ = package_;
this.type = type;
this.entry = entry;
this.id = id;
}
@Override
public String toString() {
return String.format("0x%08x", id);
}
@Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + this.id;
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResID other = (ResID) obj;
if (this.id != other.id) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.value.ResFileValue;
import brut.androlib.res.data.value.ResValue;
import brut.androlib.res.data.value.ResValueFactory;
import brut.androlib.res.data.value.ResXmlSerializable;
import brut.util.Duo;
import java.util.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResPackage {
private final ResTable mResTable;
private final int mId;
private final String mName;
private final Map<ResID, ResResSpec> mResSpecs =
new LinkedHashMap<ResID, ResResSpec>();
private final Map<ResConfigFlags, ResConfig> mConfigs =
new LinkedHashMap<ResConfigFlags, ResConfig>();
private final Map<String, ResType> mTypes =
new LinkedHashMap<String, ResType>();
private final Set<ResResource> mFiles = new LinkedHashSet<ResResource>();
private final Map<Duo<ResType, ResConfig>, ResValuesFile> mValuesFiles =
new LinkedHashMap<Duo<ResType, ResConfig>, ResValuesFile>();
private ResValueFactory mValueFactory;
public ResPackage(ResTable resTable, int id, String name) {
this.mResTable = resTable;
this.mId = id;
this.mName = name;
}
public List<ResResSpec> listResSpecs() {
return new ArrayList<ResResSpec>(mResSpecs.values());
}
public boolean hasResSpec(ResID resID) {
return mResSpecs.containsKey(resID);
}
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
ResResSpec spec = mResSpecs.get(resID);
if (spec == null) {
throw new AndrolibException("Undefined resource spec: " + resID);
}
return spec;
}
public List<ResConfig> getConfigs() {
return new ArrayList<ResConfig>(mConfigs.values());
}
public boolean hasConfig(ResConfigFlags flags) {
return mConfigs.containsKey(flags);
}
public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException {
ResConfig config = mConfigs.get(flags);
if (config == null) {
throw new AndrolibException("Undefined config: " + flags);
}
return config;
}
public List<ResType> listTypes() {
return new ArrayList<ResType>(mTypes.values());
}
public boolean hasType(String typeName) {
return mTypes.containsKey(typeName);
}
public ResType getType(String typeName) throws AndrolibException {
ResType type = mTypes.get(typeName);
if (type == null) {
throw new AndrolibException("Undefined type: " + typeName);
}
return type;
}
public Set<ResResource> listFiles() {
return mFiles;
}
public Collection<ResValuesFile> listValuesFiles() {
return mValuesFiles.values();
}
public ResTable getResTable() {
return mResTable;
}
public int getId() {
return mId;
}
public String getName() {
return mName;
}
public void addResSpec(ResResSpec spec) throws AndrolibException {
if (mResSpecs.put(spec.getId(), spec) != null) {
throw new AndrolibException("Multiple resource specs: " + spec);
}
}
public void addConfig(ResConfig config) throws AndrolibException {
if (mConfigs.put(config.getFlags(), config) != null) {
throw new AndrolibException("Multiple configs: " + config);
}
}
public void addType(ResType type) throws AndrolibException {
if (mTypes.put(type.getName(), type) != null) {
throw new AndrolibException("Multiple types: " + type);
}
}
public void addResource(ResResource res) {
ResValue value = res.getValue();
if (value instanceof ResFileValue) {
mFiles.add(res);
}
if (value instanceof ResXmlSerializable) {
ResType type = res.getResSpec().getType();
ResConfig config = res.getConfig();
Duo<ResType, ResConfig> key =
new Duo<ResType, ResConfig>(type, config);
ResValuesFile values = mValuesFiles.get(key);
if (values == null) {
values = new ResValuesFile(type, config);
mValuesFiles.put(key, values);
}
values.addResource(res);
}
}
@Override
public String toString() {
return mName;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResPackage other = (ResPackage) obj;
if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) {
return false;
}
if (this.mId != other.mId) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 37 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0);
hash = 37 * hash + this.mId;
return hash;
}
public ResValueFactory getValueFactory() {
if (mValueFactory == null) {
mValueFactory = new ResValueFactory(this);
}
return mValueFactory;
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import java.util.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResResSpec {
private final ResID mId;
private final String mName;
private final ResPackage mPackage;
private final ResType mType;
private final Map<ResConfigFlags, ResResource> mResources =
new LinkedHashMap<ResConfigFlags, ResResource>();
public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) {
this.mId = id;
this.mName = name;
this.mPackage = pkg;
this.mType = type;
}
public Set<ResResource> listResources() {
return new LinkedHashSet<ResResource>(mResources.values());
}
public ResResource getResource(ResConfig config) throws AndrolibException {
return getResource(config.getFlags());
}
public ResResource getResource(ResConfigFlags config)
throws AndrolibException {
ResResource res = mResources.get(config);
if (res == null) {
throw new AndrolibException(String.format(
"Undefined resource: spec=%s, config=%s", this, config));
}
return res;
}
public ResResource getDefaultResource() throws AndrolibException {
return getResource(new ResConfigFlags());
}
public String getFullName() {
return getFullName(false, false);
}
public String getFullName(ResPackage relativeToPackage,
boolean excludeType) {
return getFullName(
getPackage().equals(relativeToPackage), excludeType);
}
public String getFullName(boolean excludePackage, boolean excludeType) {
return
(excludePackage ? "" : getPackage().getName() + ":") +
(excludeType ? "" : getType().getName() + "/") + getName();
}
public ResID getId() {
return mId;
}
public String getName() {
return mName;
}
public ResPackage getPackage() {
return mPackage;
}
public ResType getType() {
return mType;
}
public void addResource(ResResource res)
throws AndrolibException {
ResConfigFlags flags = res.getConfig().getFlags();
if (mResources.put(flags, res) != null) {
throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", this, flags));
}
}
@Override
public String toString() {
return mId.toString() + " " + mType.toString() + "/" + mName;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.res.data.value.ResValue;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResResource {
private final ResConfig mConfig;
private final ResResSpec mResSpec;
private final ResValue mValue;
public ResResource(ResConfig config, ResResSpec spec,
ResValue value) {
this.mConfig = config;
this.mResSpec = spec;
this.mValue = value;
}
public String getFilePath() {
return mResSpec.getType().getName() +
mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
}
public ResConfig getConfig() {
return mConfig;
}
public ResResSpec getResSpec() {
return mResSpec;
}
public ResValue getValue() {
return mValue;
}
@Override
public String toString() {
return getFilePath();
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.value.ResAttr;
import brut.androlib.res.data.value.ResValue;
import brut.androlib.res.data.value.ResValueFactory;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResTable {
private final Map<Integer, ResPackage> mPackagesById =
new HashMap<Integer, ResPackage>();
private final Map<String, ResPackage> mPackagesByName =
new HashMap<String, ResPackage>();
private final Set<ResPackage> mMainPackages =
new LinkedHashSet<ResPackage>();
public ResTable() {
}
public ResResSpec getResSpec(int resID) throws AndrolibException {
return getResSpec(new ResID(resID));
}
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
return getPackage(resID.package_).getResSpec(resID);
}
public Set<ResPackage> listMainPackages() {
return mMainPackages;
}
public ResPackage getPackage(int id) throws AndrolibException {
ResPackage pkg = mPackagesById.get(id);
if (pkg == null) {
throw new AndrolibException(String.format(
"Undefined package: id=%d", id));
}
return pkg;
}
public ResPackage getPackage(String name) throws AndrolibException {
ResPackage pkg = mPackagesByName.get(name);
if (pkg == null) {
throw new AndrolibException("Undefined package: name=" + name);
}
return pkg;
}
public ResValue getValue(String package_, String type, String name)
throws AndrolibException {
return getPackage(package_).getType(type).getResSpec(name)
.getDefaultResource().getValue();
}
public void addPackage(ResPackage pkg, boolean main)
throws AndrolibException {
Integer id = pkg.getId();
if (mPackagesById.containsKey(id)) {
throw new AndrolibException(
"Multiple packages: id=" + id.toString());
}
String name = pkg.getName();
if (mPackagesByName.containsKey(name)) {
throw new AndrolibException("Multiple packages: name=" + name);
}
mPackagesById.put(id, pkg);
mPackagesByName.put(name, pkg);
if (main) {
mMainPackages.add(pkg);
}
}
}

View File

@ -0,0 +1,294 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.decoder.ResFileDecoder;
import brut.androlib.res.decoder.ResXmlSerializer;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public final class ResType {
private final String mName;
// private final TypeConfig mConfig;
private final Map<String, ResResSpec> mResSpecs =
new LinkedHashMap<String, ResResSpec>();
private final ResTable mResTable;
private final ResPackage mPackage;
public ResType(String name, ResTable resTable,
ResPackage package_) {
this.mName = name;
this.mResTable = resTable;
this.mPackage = package_;
}
public String getName() {
return mName;
}
public Set<ResResSpec> listResSpecs() {
return new LinkedHashSet<ResResSpec>(mResSpecs.values());
}
public ResResSpec getResSpec(String name) throws AndrolibException {
ResResSpec spec = mResSpecs.get(name);
if (spec == null) {
throw new AndrolibException(String.format(
"Undefined resource spec: %s/%s", getName(), name));
}
return spec;
}
//
// public void decode(AndrolibResources andRes, ResXmlSerializer serial,
// Directory in, Directory out) throws AndrolibException {
// decodeFiles(andRes, serial, in, out);
// decodeValues(andRes, serial, out);
// }
//
// public void decodeFiles(AndrolibResources andRes, ResXmlSerializer serial,
// Directory in, Directory out) throws AndrolibException {
// if (! mConfig.isFile) {
// return;
// }
//
// ResFileDecoder decoder = andRes.getResFileDecoder(serial);
//
// for (ResResSpec res : listResources()) {
// for (ResResource value : res.listResources()) {
// if (! value.isStrVal()) {
// continue;
// }
// String fileName = value.getStrVal();
// if (! fileName.startsWith("res/")) {
// throw new AndrolibException(
// "Invalid res file location: " + fileName);
// }
// decoder.decode(in, fileName.substring(4), out,
// value.getFilePath());
// }
// }
// }
//
// public void decodeValues(AndrolibResources andRes, ResXmlSerializer serial,
// Directory out) throws AndrolibException {
// if (! mConfig.isValues) {
// return;
// }
//
// boolean oldEscapeRefs = serial.setEscapeRefs(false);
//
// for (Entry<ResConfigFlags, Set<ResResource>> entry :
// groupValuesByConfig().entrySet()) {
// ResConfigFlags config = entry.getKey();
// String filePath = "values" + config.getQualifiers() + "/"
// + mConfig.valuesFileName + ".xml";
//
// try {
// serial.setOutput(out.getFileOutput(filePath), null);
// serial.startDocument(null, null);
// serial.startTag(null, "resources");
//
// for (ResResource value : entry.getValue()) {
// if (mName.equals("array")) {
// decodeArrayValue(serial, value);
// } else {
// serial.startTag(null, mConfig.valuesTagName);
// serial.attribute(
// null, "name", value.getResSpec().getName());
//
// if (mName.equals("style")) {
// decodeStyleValue(serial, value);
// } else if (mName.equals("attr")) {
// decodeAttrValue(serial, value);
// } else {
// decodeSimpleValue(serial, value);
// }
//
// serial.endTag(null, mConfig.valuesTagName);
// }
// }
//
// serial.endTag(null, "resources");
// serial.endDocument();
// } catch (IOException ex) {
// throw new AndrolibException(
// "Could not decode values for:" + filePath, ex);
// } catch (DirectoryException ex) {
// throw new AndrolibException(
// "Could not decode values for:" + filePath, ex);
// }
// }
//
// serial.setEscapeRefs(oldEscapeRefs);
// }
//
// private void decodeSimpleValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
// serial.text(value.toResXMlText());
// }
//
// private void decodeArrayValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
// String arrType = value.getBag().getType();
// String tagName = (arrType != null ? arrType + "-" : "") + "array";
// serial.startTag(null, tagName);
// serial.attribute(
// null, "name", value.getResSpec().getName());
// for (ResResource item : value.getBag().getItems().values()) {
// serial.startTag(null, "item");
// serial.text(item.getStrVal());
// serial.endTag(null, "item");
// }
// serial.endTag(null, tagName);
// }
//
// private void decodeAttrValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
//
// }
//
// private void decodeStyleValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
// ResBag bag = value.getBag();
// if (bag.getParent().id != 0) {
// serial.attribute(null, "parent", bag.getParentRes()
// .getResReference(false, mPackage.getName()));
//
// for (Entry<ResID, ResResource> entry : bag.getItems().entrySet()) {
// serial.startTag(null, "item");
//
// ResResSpec attr = mResTable.getResSpec(entry.getKey());
// serial.attribute(null, "name", attr.getResReference(false, mPackage.getName(), true).substring(1));
// serial.text(attr.getDefaultResource().decodeAttrValue(entry.getValue().getAsString(), false, mPackage.getName()));
// serial.endTag(null, "item");
// }
// }
// }
public void addResSpec(ResResSpec spec)
throws AndrolibException {
if (mResSpecs.put(spec.getName(), spec) != null) {
throw new AndrolibException(String.format(
"Multiple res specs: %s/%s", getName(), spec.getName()));
}
}
@Override
public String toString() {
return mName;
}
//
// private Map<ResConfigFlags, Set<ResResource>> groupValuesByConfig() {
// Map<ResConfigFlags, Set<ResResource>> grouped =
// new LinkedHashMap<ResConfigFlags, Set<ResResource>>();
//
// for (ResResSpec res : listResSpecs()) {
// for (ResResource value : res.listResources()) {
// ResConfigFlags config = value.getConfig().getFlags();
// Set<ResResource> values = grouped.get(config);
// if (values == null) {
// values = new LinkedHashSet<ResResource>();
// grouped.put(config, values);
// }
// values.add(value);
// }
// }
//
// return grouped;
// }
//
//
// public static ResType factory(String name, ResTable resTable,
// ResPackage package_) throws AndrolibException {
// if (typesConfig == null) {
// loadTypesConfig();
// }
// TypeConfig config = typesConfig.get(name);
// if (config == null) {
// throw new AndrolibException("Invalid type name: " + name);
// }
// return new ResType(name, config, resTable, package_);
// }
//
// private static void loadTypesConfig() {
// typesConfig = new HashMap<String, TypeConfig>();
// typesConfig.put("anim", new FileTypeConfig());
// typesConfig.put("drawable", new FileTypeConfig());
// typesConfig.put("layout", new FileTypeConfig());
// typesConfig.put("menu", new FileTypeConfig());
// typesConfig.put("raw", new FileTypeConfig());
// typesConfig.put("xml", new FileTypeConfig());
//
// typesConfig.put("attr", new ValuesTypeConfig("attrs", "attr"));
// typesConfig.put("dimen", new ValuesTypeConfig("dimens", "dimen"));
// typesConfig.put("string", new ValuesTypeConfig("strings", "string"));
// typesConfig.put("integer", new ValuesTypeConfig("integers", "integer"));
// typesConfig.put("array", new ValuesTypeConfig("arrays", "array"));
// typesConfig.put("style", new ValuesTypeConfig("styles", "style"));
//
// typesConfig.put("color", new TypeConfig(true, true, "colors", "color"));
//
// typesConfig.put("bool", new TypeConfig());
// typesConfig.put("id", new TypeConfig());
//// typesConfig.put("integer", new TypeConfig());
// typesConfig.put("plurals", new TypeConfig());
// }
//
// private static Map<String, TypeConfig> typesConfig;
//
//
// public static class TypeConfig {
// public final boolean isFile;
// public final boolean isValues;
// public final String valuesFileName;
// public final String valuesTagName;
//
// public TypeConfig() {
// this(false, false, null, null);
// }
//
// public TypeConfig(boolean isFile, boolean isValues,
// String valuesFileName, String valuesTagName) {
// this.isFile = isFile;
// this.isValues = isValues;
// this.valuesFileName = valuesFileName;
// this.valuesTagName = valuesTagName;
// }
// }
//
// public static class FileTypeConfig extends TypeConfig {
// public FileTypeConfig() {
// super(true, false, null, null);
// }
// }
//
// public static class ValuesTypeConfig extends TypeConfig {
// public ValuesTypeConfig(String fileName, String tagName) {
// super(false, true, fileName, tagName);
// }
// }
}

View File

@ -0,0 +1,85 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValuesFile {
private final ResType mType;
private final ResConfig mConfig;
private final Set<ResResource> mResources =
new LinkedHashSet<ResResource>();
public ResValuesFile(ResType type, ResConfig config) {
this.mType = type;
this.mConfig = config;
}
public String getPath() {
return "values" + mConfig.getFlags().getQualifiers()
+ "/" + mType.getName()
+ (mType.getName().endsWith("s") ? "" : "s")
+ ".xml";
}
public Set<ResResource> listResources() {
return mResources;
}
public ResType getType() {
return mType;
}
public ResConfig getConfig() {
return mConfig;
}
public void addResource(ResResource res) {
mResources.add(res);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResValuesFile other = (ResValuesFile) obj;
if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) {
return false;
}
if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + (this.mType != null ? this.mType.hashCode() : 0);
hash = 37 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
return hash;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResArrayValue extends ResBagValue implements ResXmlSerializable {
public ResArrayValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
super(parent, items);
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = getType();
type = (type == null ? "" : type + "-") + "array";
serializer.startTag(null, type);
serializer.attribute(null, "name", res.getResSpec().getName());
for (ResScalarValue item : mItems.values()) {
serializer.startTag(null, "item");
serializer.text(item.toResXmlFormat());
serializer.endTag(null, "item");
}
serializer.endTag(null, type);
}
public String getType() {
if (mItems.size() == 0) {
return null;
}
Iterator<ResScalarValue> it = mItems.values().iterator();
String type = it.next().getType();
while (it.hasNext()) {
String itemType = it.next().getType();
if (! type.equals(itemType)) {
return null;
}
}
return type;
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResAttr extends ResBagValue implements ResXmlSerializable {
private final int mType;
public ResAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items);
mType = type;
}
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
return value.toResXmlFormat();
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = getTypeAsString();
serializer.startTag(null, "attr");
serializer.attribute(null, "name", res.getResSpec().getName());
if (type != null) {
serializer.attribute(null, "format", type);
}
serializeBody(serializer, res);
serializer.endTag(null, "attr");
}
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {}
protected String getTypeAsString() {
String s = "";
if ((mType & TYPE_REFERENCE) != 0) {
s += "|reference";
}
if ((mType & TYPE_STRING) != 0) {
s += "|string";
}
if ((mType & TYPE_INT) != 0) {
s += "|integer";
}
if ((mType & TYPE_BOOL) != 0) {
s += "|boolean";
}
if ((mType & TYPE_COLOR) != 0) {
s += "|color";
}
if ((mType & TYPE_FLOAT) != 0) {
s += "|float";
}
if ((mType & TYPE_DIMEN) != 0) {
s += "|dimension";
}
if ((mType & TYPE_FRACTION) != 0) {
s += "|fraction";
}
if (s.isEmpty()) {
return null;
}
return s.substring(1);
}
private final static int TYPE_REFERENCE = 0x01;
private final static int TYPE_STRING = 0x02;
private final static int TYPE_INT = 0x04;
private final static int TYPE_BOOL = 0x08;
private final static int TYPE_COLOR = 0x10;
private final static int TYPE_FLOAT = 0x20;
private final static int TYPE_DIMEN = 0x40;
private final static int TYPE_FRACTION = 0x80;
private final static int TYPE_ANY_STRING = 0xee;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResAttrFactory {
static ResValue factory(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
int type = ((ResIntValue) items.values().iterator().next()).getValue();
int attrType = type & 0x0000ffff;
switch (type & 0x00ff0000) {
case TYPE_ENUM:
return new ResEnumAttr(parent, items, attrType);
case TYPE_SET:
return new ResSetAttr(parent, items, attrType);
}
return new ResAttr(parent, items, attrType);
}
private final static int TYPE_ENUM = 0x00010000;
private final static int TYPE_SET = 0x00020000;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResBagValue extends ResValue {
protected final ResReferenceValue mParent;
protected final Map<ResReferenceValue, ResScalarValue> mItems;
public ResBagValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
this.mParent = parent;
this.mItems = items;
}
public ResReferenceValue getParent() {
return mParent;
}
public Map<ResReferenceValue, ResScalarValue> getItems() {
return mItems;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResBoolValue extends ResScalarValue implements ResXmlSerializable {
private final boolean mValue;
public ResBoolValue(boolean value) {
super("bool");
this.mValue = value;
}
public boolean getValue() {
return mValue;
}
@Override
public String toResXmlFormat() {
return mValue ? "true" : "false";
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResColorValue extends ResIntValue {
public ResColorValue(int value) {
super(value, "color");
}
@Override
public String toResXmlFormat() {
return String.format("#%08x", mValue);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResEnumAttr extends ResMapAttr {
public ResEnumAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items, type);
}
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (value instanceof ResIntValue) {
String ret = getItemsMap().get(((ResIntValue) value).getValue());
if (ret != null) {
return ret;
}
}
return super.convertToResXmlFormat(value);
}
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (Entry<Integer, String> entry : getItemsMap().entrySet()) {
serializer.startTag(null, "enum");
serializer.attribute(null, "name", entry.getValue());
serializer.attribute(null, "value", String.valueOf(entry.getKey()));
serializer.endTag(null, "enum");
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFileValue extends ResValue {
private final String mPath;
public ResFileValue(String path) {
this.mPath = path;
}
public String getPath() {
return mPath;
}
public String getStrippedPath() throws AndrolibException {
if (! mPath.startsWith("res/")) {
throw new AndrolibException(
"File path does not start with \"res/\": " + mPath);
}
return mPath.substring(4);
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFloatValue extends ResScalarValue {
private final float mValue;
public ResFloatValue(float value) {
super("float");
this.mValue = value;
}
public float getValue() {
return mValue;
}
@Override
public String toResXmlFormat() {
return String.valueOf(mValue);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResIdValue extends ResValue {
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
serializer.startTag(null, "item");
serializer.attribute(null, "type", "id");
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item");
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResIntValue extends ResScalarValue implements ResXmlSerializable {
protected final int mValue;
public ResIntValue(int value) {
this(value, "integer");
}
public ResIntValue(int value, String type) {
super(type);
this.mValue = value;
}
public int getValue() {
return mValue;
}
@Override
public String toResXmlFormat() throws AndrolibException {
return String.valueOf(mValue);
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public abstract class ResMapAttr extends ResAttr {
private Map<Integer, String> mMap;
public ResMapAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items, type);
}
protected Map<Integer, String> getItemsMap() throws AndrolibException {
if (mMap == null) {
loadItemsMap();
}
return mMap;
}
private void loadItemsMap() throws AndrolibException {
mMap = new LinkedHashMap<Integer, String>();
Iterator<Entry<ResReferenceValue, ResScalarValue>> it =
mItems.entrySet().iterator();
it.next();
while (it.hasNext()) {
Entry<ResReferenceValue, ResScalarValue> entry = it.next();
// TODO
if (entry.getKey().getValue() < 0x01010000) {
continue;
}
mMap.put(
((ResIntValue) entry.getValue()).getValue(),
entry.getKey().getReferent().getName());
}
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResPluralsValue extends ResBagValue implements ResXmlSerializable {
public ResPluralsValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
super(parent, items);
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
serializer.startTag(null, "plurals");
serializer.attribute(null, "name", res.getResSpec().getName());
for (Entry<String, String> entry : getPluralsMap().entrySet()) {
serializer.startTag(null, "item");
serializer.attribute(null, "quantity", entry.getKey());
serializer.text(entry.getValue());
serializer.endTag(null, "item");
}
serializer.endTag(null, "plurals");
}
private Map<String, String> getPluralsMap() {
Map<String, String> plurals = new LinkedHashMap<String, String>();
for (Entry<ResReferenceValue, ResScalarValue> entry
: mItems.entrySet()) {
String quantity = getQuantityMap()[
(entry.getKey().getValue() & 0xffff) - 4];
if (quantity != null) {
String value = ((ResStringValue) entry.getValue()).getValue();
plurals.put(quantity, AndrolibResources.escapeForResXml(value));
}
}
return plurals;
}
private static String[] getQuantityMap() {
if (quantityMap == null) {
quantityMap = new String[]
{"other", "zero", "one", "two", "few", "many"};
}
return quantityMap;
}
private static String[] quantityMap;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResResSpec;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResReferenceValue extends ResIntValue {
private final ResPackage mPackage;
private final boolean mTheme;
public ResReferenceValue(ResPackage package_, int value) {
this(package_, value, false);
}
public ResReferenceValue(ResPackage package_, int value, boolean theme) {
super(value);
mPackage = package_;
mTheme = theme;
}
@Override
public String toResXmlFormat() throws AndrolibException {
if (isNull()) {
return "@null";
}
// try {
return
(mTheme ? '?' : '@') +
getReferent().getFullName(mPackage, mTheme);
// } catch (AndrolibException ex) {
// return "@" + String.valueOf(mValue);
// }
}
public ResResSpec getReferent() throws AndrolibException {
return mPackage.getResTable().getResSpec(getValue());
}
public boolean isNull() {
return mValue == 0;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public abstract class ResScalarValue extends ResValue
implements ResXmlPrintable, ResXmlSerializable {
protected final String mType;
protected ResScalarValue(String type) {
mType = type;
}
public abstract String toResXmlFormat() throws AndrolibException;
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String tagName = res.getResSpec().getType().getName();
serializer.startTag(null, tagName);
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.text(toResXmlFormat());
serializer.endTag(null, tagName);
}
public String getType() {
return mType;
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResSetAttr extends ResMapAttr {
public ResSetAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items, type);
}
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (! (value instanceof ResIntValue)) {
return super.convertToResXmlFormat(value);
}
int intVal = ((ResIntValue) value).getValue();
String strVal = "";
for (Entry<Integer, String> entry : getItemsMap().entrySet()) {
int flag = entry.getKey();
if ((intVal & flag) == flag) {
strVal += "|" + entry.getValue();
}
}
if (strVal.isEmpty()) {
return "";
}
return strVal.substring(1);
}
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (Entry<Integer, String> entry : getItemsMap().entrySet()) {
serializer.startTag(null, "flag");
serializer.attribute(null, "name", entry.getValue());
serializer.attribute(null, "value",
String.format("0x%08x", entry.getKey()));
serializer.endTag(null, "flag");
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.res.AndrolibResources;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResStringValue extends ResScalarValue
implements ResXmlSerializable {
private final String mValue;
public ResStringValue(String value) {
super("string");
this.mValue = value;
}
public String getValue() {
return mValue;
}
@Override
public String toResXmlFormat() {
if (mValue.isEmpty()) {
return "";
}
return AndrolibResources.escapeForResXml(mValue);
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResSpec;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResStyleValue extends ResBagValue implements ResXmlSerializable {
public ResStyleValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
super(parent, items);
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
serializer.startTag(null, "style");
serializer.attribute(null, "name", res.getResSpec().getName());
if (! mParent.isNull()) {
serializer.attribute(null, "parent", mParent.toResXmlFormat());
}
for (Entry<ResReferenceValue, ResScalarValue> entry
: mItems.entrySet()) {
ResResSpec spec = entry.getKey().getReferent();
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
String value = attr.convertToResXmlFormat(entry.getValue());
if (value == null) {
continue;
}
serializer.startTag(null, "item");
serializer.attribute(null, "name",
spec.getFullName(res.getResSpec().getPackage(), true));
serializer.text(value);
serializer.endTag(null, "item");
}
serializer.endTag(null, "style");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValue {
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTable;
import brut.androlib.res.data.ResType;
import brut.androlib.res.jni.JniBagItem;
import brut.androlib.res.jni.JniEntry;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValueFactory {
private final ResPackage mPackage;
public ResValueFactory(ResPackage pakage_) {
this.mPackage = pakage_;
}
public ResScalarValue factory(String string) {
if (string.isEmpty()) {
return new ResStringValue(string);
}
char c = string.charAt(0);
if (c == '@' || c == '?') {
return newReference(
Integer.parseInt(string.substring(1)), c == '?');
}
if (c == '#') {
return new ResColorValue(
(int) Long.parseLong(string.substring(1), 16));
}
try {
if (string.startsWith("0x")) {
return new ResIntValue(
(int) Long.parseLong(string.substring(2), 16));
}
return new ResIntValue(Integer.parseInt(string));
} catch (NumberFormatException ex) {}
return new ResStringValue(string);
}
public ResValue factory(JniEntry entry)
throws AndrolibException {
if ("id".equals(entry.type)) {
return new ResIdValue();
}
switch (entry.valueType) {
case TYPE_BAG:
return bagFactory(entry);
case TYPE_REFERENCE:
return newReference(entry.intVal);
case TYPE_ATTRIBUTE:
return newReference(entry.intVal, true);
case TYPE_INT_BOOLEAN:
return new ResBoolValue(entry.boolVal);
case TYPE_INT_DEC:
case TYPE_INT_HEX:
return new ResIntValue(entry.intVal);
case TYPE_FLOAT:
return new ResFloatValue(entry.floatVal);
case TYPE_INT_COLOR_ARGB4:
case TYPE_INT_COLOR_ARGB8:
case TYPE_INT_COLOR_RGB4:
case TYPE_INT_COLOR_RGB8:
return new ResColorValue(entry.intVal);
case TYPE_STRING:
if (entry.strVal.startsWith("res/")) {
return new ResFileValue(entry.strVal);
}
case TYPE_DIMENSION:
case TYPE_FRACTION:
return new ResStringValue(entry.strVal);
}
throw new AndrolibException(String.format(
"Unknown value type for %s/%s: ",
entry.type, entry.name, String.valueOf(entry.valueType)));
}
private ResValue bagFactory(JniEntry entry)
throws AndrolibException {
ResReferenceValue parent = newReference(entry.bagParent);
Map<ResReferenceValue, ResScalarValue> items =
convertItems(entry.bagItems);
String type = entry.type;
if ("array".equals(type)) {
return new ResArrayValue(parent, items);
}
if ("style".equals(type)) {
return new ResStyleValue(parent, items);
}
if ("plurals".equals(type)) {
return new ResPluralsValue(parent, items);
}
if ("attr".equals(type)) {
return ResAttrFactory.factory(parent, items);
}
return new ResBagValue(parent, items);
}
private ResReferenceValue newReference(int resID) {
return newReference(resID, false);
}
private ResReferenceValue newReference(int resID, boolean theme) {
return new ResReferenceValue(mPackage, resID, theme);
}
private Map<ResReferenceValue, ResScalarValue> convertItems(
JniBagItem[] jniItems) throws AndrolibException {
Map<ResReferenceValue, ResScalarValue> items =
new LinkedHashMap<ResReferenceValue, ResScalarValue>();
for (int i = 0; i < jniItems.length; i++) {
JniBagItem jniItem = jniItems[i];
items.put(newReference(jniItem.resID),
(ResScalarValue) factory(jniItem.entry));
}
return items;
}
private final static int TYPE_NULL = 0x00;
private final static int TYPE_REFERENCE = 0x01;
private final static int TYPE_ATTRIBUTE = 0x02;
private final static int TYPE_STRING = 0x03;
private final static int TYPE_FLOAT = 0x04;
private final static int TYPE_DIMENSION = 0x05;
private final static int TYPE_FRACTION = 0x06;
private final static int TYPE_INT_DEC = 0x10;
private final static int TYPE_INT_HEX = 0x11;
private final static int TYPE_INT_BOOLEAN = 0x12;
private final static int TYPE_INT_COLOR_ARGB8 = 0x1c;
private final static int TYPE_INT_COLOR_RGB8 = 0x1d;
private final static int TYPE_INT_COLOR_ARGB4 = 0x1e;
private final static int TYPE_INT_COLOR_RGB4 = 0x1f;
private final static int TYPE_BAG = -0x01;
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResXmlPrintable {
public String toResXmlFormat() throws AndrolibException;
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResXmlSerializable {
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException;
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.*;
import brut.androlib.res.data.value.ResValue;
import brut.androlib.res.data.value.ResValueFactory;
import brut.androlib.res.jni.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniPackageDecoder {
public ResPackage decode(JniPackage jniPkg, ResTable resTable)
throws AndrolibException {
ResPackage pkg = new ResPackage(resTable, jniPkg.id, jniPkg.name);
ResValueFactory valueFactory = pkg.getValueFactory();
JniConfig[] jniConfigs = jniPkg.configs;
for (int i = 0; i < jniConfigs.length; i++) {
JniConfig jniConfig = jniConfigs[i];
ResConfigFlags flags = new ResConfigFlags(jniConfig);
ResConfig config;
if (pkg.hasConfig(flags)) {
config = pkg.getConfig(flags);
} else {
config = new ResConfig(flags);
pkg.addConfig(config);
}
JniEntry[] jniEntries = jniConfig.entries;
for (int j = 0; j < jniEntries.length; j++) {
JniEntry jniEntry = jniEntries[j];
ResType type;
String typeName = jniEntry.type;
if (pkg.hasType(typeName)) {
type = pkg.getType(typeName);
} else {
type = new ResType(typeName, resTable, pkg);
pkg.addType(type);
}
ResID resID = new ResID(jniEntry.resID);
ResResSpec spec;
if (pkg.hasResSpec(resID)) {
spec = pkg.getResSpec(resID);
} else {
spec = new ResResSpec(resID, jniEntry.name, pkg, type);
pkg.addResSpec(spec);
type.addResSpec(spec);
}
ResValue value = valueFactory.factory(jniEntry);
ResResource res =
new ResResource(config, spec, value);
config.addResource(res);
spec.addResource(res);
pkg.addResource(res);
}
}
return pkg;
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFileDecoder {
private final ResStreamDecoderContainer mDecoders;
public ResFileDecoder(ResStreamDecoderContainer decoders) {
this.mDecoders = decoders;
}
public void decode(Directory inDir, String inFileName, Directory outDir,
String outResName) throws AndrolibException {
String ext = "";
int extPos = inFileName.lastIndexOf(".");
if (extPos != -1) {
ext = inFileName.substring(extPos);
}
if (inFileName.startsWith("raw/")) {
decode(inDir, inFileName, outDir, outResName + ext, "raw");
return;
}
if (inFileName.endsWith(".9.png")) {
decode(inDir, inFileName, outDir, outResName + ".png", "raw");
return;
}
if (inFileName.endsWith(".xml")) {
decode(inDir, inFileName, outDir, outResName + ".xml", "xml");
return;
}
if (inFileName.endsWith(".html")) {
decode(inDir, inFileName, outDir, outResName + ".html", "xml");
return;
}
decode(inDir, inFileName, outDir, outResName + ext, "raw");
}
public void decode(Directory inDir, String inFileName, Directory outDir,
String outFileName, String decoder) throws AndrolibException {
try {
InputStream in = inDir.getFileInput(inFileName);
OutputStream out = outDir.getFileOutput(outFileName);
mDecoders.decode(in, out, decoder);
in.close();
out.close();
} catch (AndrolibException ex) {
throw new AndrolibException(String.format(
"Could not decode file \"%s\" to \"%s\"",
inFileName, outFileName), ex);
} catch (IOException ex) {
throw new AndrolibException(String.format(
"Could not decode file \"%s\" to \"%s\"",
inFileName, outFileName), ex);
} catch (DirectoryException ex) {
throw new AndrolibException(String.format(
"Could not decode file \"%s\" to \"%s\"",
inFileName, outFileName), ex);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResRawStreamDecoder implements ResStreamDecoder {
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
IOUtils.copy(in, out);
} catch (IOException ex) {
throw new AndrolibException("Could not decode raw stream", ex);
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResStreamDecoder {
public void decode(InputStream in, OutputStream out)
throws AndrolibException;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResStreamDecoderContainer {
private final Map<String, ResStreamDecoder> mDecoders =
new HashMap<String, ResStreamDecoder>();
public void decode(InputStream in, OutputStream out, String decoderName)
throws AndrolibException {
getDecoder(decoderName).decode(in, out);
}
public ResStreamDecoder getDecoder(String name) throws AndrolibException {
ResStreamDecoder decoder = mDecoders.get(name);
if (decoder== null) {
throw new AndrolibException("Undefined decoder: " + name);
}
return decoder;
}
public void setDecoder(String name, ResStreamDecoder decoder) {
mDecoders.put(name, decoder);
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.*;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.value.ResAttr;
import java.io.IOException;
import org.xmlpull.mxp1_serializer.MXSerializer;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResXmlSerializer extends MXSerializer {
private final static String RES_NAMESPACE =
"http://schemas.android.com/apk/res/android";
private ResPackage mCurrentPackage;
private boolean mDecodingEnabled = true;
@Override
public XmlSerializer attribute(String namespace, String name, String value)
throws IOException, IllegalArgumentException, IllegalStateException
{
if (! mDecodingEnabled) {
return super.attribute(namespace, name, value);
}
if (namespace == null || namespace.isEmpty()) {
return super.attribute(namespace, name,
// AndrolibResources.escapeForResXml(value)
value
);
}
String pkgName = RES_NAMESPACE.equals(namespace) ?
"android" : mCurrentPackage.getName();
try {
ResAttr attr = (ResAttr) mCurrentPackage.getResTable()
.getValue(pkgName, "attr", name);
value = attr.convertToResXmlFormat(
mCurrentPackage.getValueFactory().factory(value));
} catch (AndrolibException ex) {
throw new IllegalArgumentException(String.format(
"could not decode attribute: ns=%s, name=%s, value=%s",
getPrefix(namespace, false), name, value), ex);
}
if (value == null) {
return this;
}
// if ("id".equals(name) && value.startsWith("@id")) {
if (value.startsWith("@id")) {
value = "@+id" + value.substring(3);
}
return super.attribute(namespace, name, value);
}
@Override
public XmlSerializer text(String text) throws IOException {
if (mDecodingEnabled) {
text = AndrolibResources.escapeForResXml(text);
}
return super.text(text);
}
@Override
public XmlSerializer text(char[] buf, int start, int len) throws IOException {
if (mDecodingEnabled) {
return this.text(new String(buf, start, len));
}
return super.text(buf, start, len);
}
@Override
public void startDocument(String encoding, Boolean standalone) throws
IOException, IllegalArgumentException, IllegalStateException {
super.startDocument(encoding != null ? encoding : "UTF-8", standalone);
super.out.write("\n");
super.setPrefix("android", RES_NAMESPACE);
}
public void setCurrentPackage(ResPackage package_) {
mCurrentPackage = package_;
}
public boolean setDecodingEnabled(boolean escapeRefs) {
boolean oldVal = mDecodingEnabled;
this.mDecodingEnabled = escapeRefs;
return oldVal;
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.decoder;
import android.content.res.XmlBlock;
import brut.androlib.AndrolibException;
import java.io.*;
import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.*;
import org.xmlpull.v1.wrapper.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResXmlStreamDecoder implements ResStreamDecoder {
private final ResXmlSerializer mSerializer;
public ResXmlStreamDecoder(ResXmlSerializer serializer) {
this.mSerializer = serializer;
}
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
XmlPullParserWrapper par =
getResXmlParserWrapper(in);
XmlSerializerWrapper ser =
getXmlWrapperFactory().newSerializerWrapper(mSerializer);
ser.setOutput(out, null);
mSerializer.setDecodingEnabled(true);
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
ser.event(par);
}
ser.flush();
} catch (IOException ex) {
throw new AndrolibException("could not decode XML stream", ex);
} catch (IllegalArgumentException ex) {
throw new AndrolibException("could not decode XML stream", ex);
} catch (IllegalStateException ex) {
throw new AndrolibException("could not decode XML stream", ex);
} catch (XmlPullParserException ex) {
throw new AndrolibException("could not decode XML stream", ex);
}
}
private XmlPullParserWrapper getResXmlParserWrapper(InputStream in)
throws IOException, XmlPullParserException {
XmlBlock xml = new XmlBlock(copyStreamToByteArray(in));
XmlPullParser parser = xml.newParser();
return getXmlWrapperFactory().newPullParserWrapper(parser);
}
private XmlPullWrapperFactory getXmlWrapperFactory()
throws XmlPullParserException {
return XmlPullWrapperFactory.newInstance();
}
private byte[] copyStreamToByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copy(in, out);
return out.toByteArray();
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniBagItem {
public final int resID;
public final JniEntry entry;
public JniBagItem(int resID, JniEntry entry) {
this.resID = resID;
this.entry = entry;
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniConfig {
public final int mcc;
public final int mnc;
public final char[] language;
public final char[] country;
public final int orientation;
public final int touchscreen;
public final int density;
public final int keyboard;
public final int navigation;
public final int inputFlags;
public final int screenWidth;
public final int screenHeight;
public final int screenLayout;
public final int sdkVersion;
public final JniEntry[] entries;
public JniConfig(int mcc, int mnc, char[] language, char[] country,
int orientation, int touchscreen, int density, int keyboard,
int navigation, int inputFlags, int screenWidth, int screenHeight,
int screenLayout, int sdkVersion, JniEntry[] entries) {
this.mcc = mcc;
this.mnc = mnc;
this.language = language;
this.country = country;
this.orientation = orientation;
this.touchscreen = touchscreen;
this.density = density;
this.keyboard = keyboard;
this.navigation = navigation;
this.inputFlags = inputFlags;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.screenLayout = screenLayout;
this.sdkVersion = sdkVersion;
this.entries = entries;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniEntry {
public final int resID;
public final String name;
public final String type;
public final int valueType;
public final boolean boolVal;
public final int intVal;
public final float floatVal;
public final String strVal;
public final int bagParent;
public final JniBagItem[] bagItems;
public JniEntry(int resID, String name, String type, int valueType,
boolean boolVal, int intVal, float floatVal, String strVal,
int bagParent, JniBagItem[] bagItems) {
this.resID = resID;
this.name = name;
this.type = type;
this.valueType = valueType;
this.boolVal = boolVal;
this.intVal = intVal;
this.floatVal = floatVal;
this.strVal = strVal;
this.bagParent = bagParent;
this.bagItems = bagItems;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniPackage {
public final int id;
public final String name;
public final JniConfig[] configs;
public JniPackage(int id, String name, JniConfig[] configs) {
this.id = id;
this.name = name;
this.configs = configs;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniPackageGroup {
public final JniPackage[] packages;
public JniPackageGroup(JniPackage[] packages) {
this.packages = packages;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* 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.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniType {
public final int entryCount;
public final int typeSpecFlags;
public final JniConfig[] configs;
public JniType(int entryCount, int typeSpecFlags, JniConfig[] configs) {
this.entryCount = entryCount;
this.typeSpecFlags = typeSpecFlags;
this.configs = configs;
}
}

BIN
src/resources.arsc Normal file

Binary file not shown.