Code cleanup of 2013

Signed-off-by: Connor Tumbleson <connor.tumbleson@gmail.com>
This commit is contained in:
Connor Tumbleson 2013-02-12 21:12:17 -06:00
parent f504ceca43
commit e82c0754de
71 changed files with 7649 additions and 7444 deletions

View File

@ -36,197 +36,200 @@ import java.util.logging.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class Main {
public static void main(String[] args)
throws IOException, InterruptedException, BrutException {
try {
Verbosity verbosity = Verbosity.NORMAL;
int i;
for (i = 0; i < args.length; i++) {
String opt = args[i];
if (opt.startsWith("--version") || (opt.startsWith("-version"))) {
version_print();
System.exit(1);
}
if (! opt.startsWith("-")) {
break;
}
if ("-v".equals(opt) || "--verbose".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.VERBOSE;
} else if ("-q".equals(opt) || "--quiet".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.QUIET;
} else {
throw new InvalidArgsError();
}
}
setupLogging(verbosity);
public static void main(String[] args) throws IOException,
InterruptedException, BrutException {
try {
Verbosity verbosity = Verbosity.NORMAL;
int i;
for (i = 0; i < args.length; i++) {
String opt = args[i];
if (args.length <= i) {
throw new InvalidArgsError();
}
String cmd = args[i];
args = Arrays.copyOfRange(args, i + 1, args.length);
if (opt.startsWith("--version") || (opt.startsWith("-version"))) {
version_print();
System.exit(1);
}
if (!opt.startsWith("-")) {
break;
}
if ("-v".equals(opt) || "--verbose".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.VERBOSE;
} else if ("-q".equals(opt) || "--quiet".equals(opt)) {
if (verbosity != Verbosity.NORMAL) {
throw new InvalidArgsError();
}
verbosity = Verbosity.QUIET;
} else {
throw new InvalidArgsError();
}
}
setupLogging(verbosity);
if ("d".equals(cmd) || "decode".equals(cmd)) {
cmdDecode(args);
} else if ("b".equals(cmd) || "build".equals(cmd)) {
cmdBuild(args);
} else if ("if".equals(cmd) || "install-framework".equals(cmd)) {
cmdInstallFramework(args);
} else if ("publicize-resources".equals(cmd)) {
cmdPublicizeResources(args);
} else {
throw new InvalidArgsError();
}
} catch (InvalidArgsError ex) {
usage();
System.exit(1);
}
}
if (args.length <= i) {
throw new InvalidArgsError();
}
String cmd = args[i];
args = Arrays.copyOfRange(args, i + 1, args.length);
private static void cmdDecode(String[] args) throws InvalidArgsError,
AndrolibException {
ApkDecoder decoder = new ApkDecoder();
if ("d".equals(cmd) || "decode".equals(cmd)) {
cmdDecode(args);
} else if ("b".equals(cmd) || "build".equals(cmd)) {
cmdBuild(args);
} else if ("if".equals(cmd) || "install-framework".equals(cmd)) {
cmdInstallFramework(args);
} else if ("publicize-resources".equals(cmd)) {
cmdPublicizeResources(args);
} else {
throw new InvalidArgsError();
}
} catch (InvalidArgsError ex) {
usage();
System.exit(1);
}
}
int i;
for (i = 0; i < args.length; i++) {
String opt = args[i];
if (! opt.startsWith("-")) {
break;
}
if ("-s".equals(opt) || "--no-src".equals(opt)) {
decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE);
} else if ("-d".equals(opt) || "--debug".equals(opt)) {
decoder.setDebugMode(true);
} else if ("-b".equals(opt) || "--no-debug-info".equals(opt)) {
decoder.setBaksmaliDebugMode(false);
} else if ("-t".equals(opt) || "--frame-tag".equals(opt)) {
i++;
if (i >= args.length) {
throw new InvalidArgsError();
}
decoder.setFrameworkTag(args[i]);
} else if ("-f".equals(opt) || "--force".equals(opt)) {
decoder.setForceDelete(true);
} else if ("-r".equals(opt) || "--no-res".equals(opt)) {
decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE);
} else if ("--keep-broken-res".equals(opt)) {
decoder.setKeepBrokenResources(true);
} else if ("--frame-path".equals(opt)) {
i++;
System.out.println("Using Framework Directory: " + args[i]);
decoder.setFrameworkDir(args[i]);
} else {
throw new InvalidArgsError();
}
}
private static void cmdDecode(String[] args) throws InvalidArgsError,
AndrolibException {
ApkDecoder decoder = new ApkDecoder();
String outName = null;
if (args.length == i + 2) {
outName = args[i + 1];
} else if (args.length == i + 1) {
outName = args[i];
outName = outName.endsWith(".apk") ?
outName.substring(0, outName.length() - 4) : outName + ".out";
outName = new File(outName).getName();
} else {
throw new InvalidArgsError();
}
File outDir = new File(outName);
decoder.setOutDir(outDir);
decoder.setApkFile(new File(args[i]));
int i;
for (i = 0; i < args.length; i++) {
String opt = args[i];
if (!opt.startsWith("-")) {
break;
}
if ("-s".equals(opt) || "--no-src".equals(opt)) {
decoder.setDecodeSources(ApkDecoder.DECODE_SOURCES_NONE);
} else if ("-d".equals(opt) || "--debug".equals(opt)) {
decoder.setDebugMode(true);
} else if ("-b".equals(opt) || "--no-debug-info".equals(opt)) {
decoder.setBaksmaliDebugMode(false);
} else if ("-t".equals(opt) || "--frame-tag".equals(opt)) {
i++;
if (i >= args.length) {
throw new InvalidArgsError();
}
decoder.setFrameworkTag(args[i]);
} else if ("-f".equals(opt) || "--force".equals(opt)) {
decoder.setForceDelete(true);
} else if ("-r".equals(opt) || "--no-res".equals(opt)) {
decoder.setDecodeResources(ApkDecoder.DECODE_RESOURCES_NONE);
} else if ("--keep-broken-res".equals(opt)) {
decoder.setKeepBrokenResources(true);
} else if ("--frame-path".equals(opt)) {
i++;
System.out.println("Using Framework Directory: " + args[i]);
decoder.setFrameworkDir(args[i]);
} else {
throw new InvalidArgsError();
}
}
try {
decoder.decode();
} catch (OutDirExistsException ex) {
System.out.println(
"Destination directory (" + outDir.getAbsolutePath() + ") " +
"already exists. Use -f switch if you want to overwrite it.");
System.exit(1);
} catch (InFileNotFoundException ex) {
System.out.println(
"Input file (" + args[i] + ") " +
"was not found or was not readable.");
System.exit(1);
} catch (CantFindFrameworkResException ex) {
System.out.println(
"Can't find framework resources for package of id: " +
String.valueOf(ex.getPkgId()) + ". You must install proper " +
"framework files, see project website for more info.");
System.exit(1);
} catch (IOException ex) {
System.out.println(
"Could not modify file. Please ensure you have permission.");
System.exit(1);
}
}
String outName = null;
if (args.length == i + 2) {
outName = args[i + 1];
} else if (args.length == i + 1) {
outName = args[i];
outName = outName.endsWith(".apk") ? outName.substring(0,
outName.length() - 4) : outName + ".out";
outName = new File(outName).getName();
} else {
throw new InvalidArgsError();
}
File outDir = new File(outName);
decoder.setOutDir(outDir);
decoder.setApkFile(new File(args[i]));
private static void cmdBuild(String[] args) throws BrutException {
// hold all the fields
HashMap<String, Boolean> flags = new HashMap<String, Boolean>();
flags.put("forceBuildAll", false);
flags.put("debug", false);
flags.put("verbose", false);
flags.put("injectOriginal", false);
flags.put("framework", false);
flags.put("update", false);
int i;
int skip = 0;
ExtFile mOrigApk = null;
String mAaptPath = "";
for (i = 0; i < args.length; i++) {
String opt = args[i];
if (! opt.startsWith("-")) {
break;
}
if ("-f".equals(opt) || "--force-all".equals(opt)) {
flags.put("forceBuildAll", true);
} else if ("-d".equals(opt) || "--debug".equals(opt)) {
flags.put("debug", true);
} else if ("-v".equals(opt) || "--verbose".equals(opt)) {
flags.put("verbose", true);
} else if ("-a".equals(opt) || "--aapt".equals(opt)) {
mAaptPath = args[i + 1];
skip = 1;
} else if ("-o".equals(opt) || "--original".equals(opt)) {
if (args.length >= 4) {
throw new InvalidArgsError();
} else {
flags.put("injectOriginal", true);
mOrigApk = new ExtFile(args[i + 1]);
skip = 1;
}
} else {
throw new InvalidArgsError();
}
}
try {
decoder.decode();
} catch (OutDirExistsException ex) {
System.out
.println("Destination directory ("
+ outDir.getAbsolutePath()
+ ") "
+ "already exists. Use -f switch if you want to overwrite it.");
System.exit(1);
} catch (InFileNotFoundException ex) {
System.out.println("Input file (" + args[i] + ") "
+ "was not found or was not readable.");
System.exit(1);
} catch (CantFindFrameworkResException ex) {
System.out
.println("Can't find framework resources for package of id: "
+ String.valueOf(ex.getPkgId())
+ ". You must install proper "
+ "framework files, see project website for more info.");
System.exit(1);
} catch (IOException ex) {
System.out
.println("Could not modify file. Please ensure you have permission.");
System.exit(1);
}
String appDirName;
File outFile = null;
switch (args.length - i - skip) {
case 0:
appDirName = ".";
break;
case 2:
outFile = new File(args[i + 1 + skip]);
case 1:
appDirName = args[i + skip];
break;
default:
throw new InvalidArgsError();
}
new Androlib().build(new File(appDirName), outFile, flags, mOrigApk, mAaptPath);
}
}
private static void cmdBuild(String[] args) throws BrutException {
// hold all the fields
HashMap<String, Boolean> flags = new HashMap<String, Boolean>();
flags.put("forceBuildAll", false);
flags.put("debug", false);
flags.put("verbose", false);
flags.put("injectOriginal", false);
flags.put("framework", false);
flags.put("update", false);
int i;
int skip = 0;
ExtFile mOrigApk = null;
String mAaptPath = "";
for (i = 0; i < args.length; i++) {
String opt = args[i];
if (!opt.startsWith("-")) {
break;
}
if ("-f".equals(opt) || "--force-all".equals(opt)) {
flags.put("forceBuildAll", true);
} else if ("-d".equals(opt) || "--debug".equals(opt)) {
flags.put("debug", true);
} else if ("-v".equals(opt) || "--verbose".equals(opt)) {
flags.put("verbose", true);
} else if ("-a".equals(opt) || "--aapt".equals(opt)) {
mAaptPath = args[i + 1];
skip = 1;
} else if ("-o".equals(opt) || "--original".equals(opt)) {
if (args.length >= 4) {
throw new InvalidArgsError();
} else {
flags.put("injectOriginal", true);
mOrigApk = new ExtFile(args[i + 1]);
skip = 1;
}
} else {
throw new InvalidArgsError();
}
}
String appDirName;
File outFile = null;
switch (args.length - i - skip) {
case 0:
appDirName = ".";
break;
case 2:
outFile = new File(args[i + 1 + skip]);
case 1:
appDirName = args[i + skip];
break;
default:
throw new InvalidArgsError();
}
new Androlib().build(new File(appDirName), outFile, flags, mOrigApk,
mAaptPath);
}
private static void cmdInstallFramework(String[] args)
throws AndrolibException {
@ -254,120 +257,123 @@ public class Main {
throw new InvalidArgsError();
}
private static void cmdPublicizeResources(String[] args)
throws InvalidArgsError, AndrolibException {
if (args.length != 1) {
throw new InvalidArgsError();
}
private static void cmdPublicizeResources(String[] args)
throws InvalidArgsError, AndrolibException {
if (args.length != 1) {
throw new InvalidArgsError();
}
new Androlib().publicizeResources(new File(args[0]));
}
new Androlib().publicizeResources(new File(args[0]));
}
private static void version_print() {
System.out.println(
Androlib.getVersion());
}
private static void usage() {
System.out.println(
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" +
"with smali v" + ApktoolProperties.get("smaliVersion") +
", and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" +
"Updated by @iBotPeaches <connor.tumbleson@gmail.com> \n" +
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
"\n" +
"Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" +
"\n" +
"COMMANDs are:\n" +
"\n" +
" d[ecode] [OPTS] <file.apk> [<dir>]\n" +
" Decode <file.apk> to <dir>.\n" +
"\n" +
" OPTS:\n" +
"\n" +
" -s, --no-src\n" +
" Do not decode sources.\n" +
" -r, --no-res\n" +
" Do not decode resources.\n" +
" -d, --debug\n" +
" Decode in debug mode. Check project page for more info.\n" +
" -b, --no-debug-info\n" +
" Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n" +
" -f, --force\n" +
" Force delete destination directory.\n" +
" -t <tag>, --frame-tag <tag>\n" +
" Try to use framework files tagged by <tag>.\n" +
" --frame-path <dir>\n" +
" Use the specified directory for framework files\n" +
" --keep-broken-res\n" +
" Use if there was an error and some resources were dropped, e.g.:\n" +
" \"Invalid config flags detected. Dropping resources\", but you\n" +
" want to decode them anyway, even with errors. You will have to\n" +
" fix them manually before building." +
"\n\n" +
" b[uild] [OPTS] [<app_path>] [<out_file>]\n" +
" Build an apk from already decoded application located in <app_path>.\n" +
"\n" +
" It will automatically detect, whether files was changed and perform\n" +
" needed steps only.\n" +
"\n" +
" If you omit <app_path> then current directory will be used.\n" +
" If you omit <out_file> then <app_path>/dist/<name_of_original.apk>\n" +
" will be used.\n" +
"\n" +
" OPTS:\n" +
"\n" +
" -f, --force-all\n" +
" Skip changes detection and build all files.\n" +
" -d, --debug\n" +
" Build in debug mode. Check project page for more info.\n" +
" -a, --aapt\n" +
" Loads aapt from specified location.\n" +
"\n" +
" if|install-framework <framework.apk> [<tag>] --frame-path [<location>] \n" +
" Install framework file to your system.\n" +
"\n" +
"For additional info, see: http://code.google.com/p/android-apktool/" +
"\n" +
"For smali/baksmali info, see: http://code.google.com/p/smali/"
);
}
private static void version_print() {
System.out.println(Androlib.getVersion());
}
private static void setupLogging(Verbosity verbosity) {
Logger logger = Logger.getLogger("");
for (Handler handler : logger.getHandlers()) {
logger.removeHandler(handler);
}
if (verbosity == Verbosity.QUIET) {
return;
}
private static void usage() {
System.out
.println("Apktool v"
+ Androlib.getVersion()
+ " - a tool for reengineering Android apk files\n"
+ "Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n"
+ "with smali v"
+ ApktoolProperties.get("smaliVersion")
+ ", and baksmali v"
+ ApktoolProperties.get("baksmaliVersion")
+ "\n"
+ "Updated by @iBotPeaches <connor.tumbleson@gmail.com> \n"
+ "Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n"
+ "\n"
+ "Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n"
+ "\n"
+ "COMMANDs are:\n"
+ "\n"
+ " d[ecode] [OPTS] <file.apk> [<dir>]\n"
+ " Decode <file.apk> to <dir>.\n"
+ "\n"
+ " OPTS:\n"
+ "\n"
+ " -s, --no-src\n"
+ " Do not decode sources.\n"
+ " -r, --no-res\n"
+ " Do not decode resources.\n"
+ " -d, --debug\n"
+ " Decode in debug mode. Check project page for more info.\n"
+ " -b, --no-debug-info\n"
+ " Baksmali -- don't write out debug info (.local, .param, .line, etc.)\n"
+ " -f, --force\n"
+ " Force delete destination directory.\n"
+ " -t <tag>, --frame-tag <tag>\n"
+ " Try to use framework files tagged by <tag>.\n"
+ " --frame-path <dir>\n"
+ " Use the specified directory for framework files\n"
+ " --keep-broken-res\n"
+ " Use if there was an error and some resources were dropped, e.g.:\n"
+ " \"Invalid config flags detected. Dropping resources\", but you\n"
+ " want to decode them anyway, even with errors. You will have to\n"
+ " fix them manually before building."
+ "\n\n"
+ " b[uild] [OPTS] [<app_path>] [<out_file>]\n"
+ " Build an apk from already decoded application located in <app_path>.\n"
+ "\n"
+ " It will automatically detect, whether files was changed and perform\n"
+ " needed steps only.\n"
+ "\n"
+ " If you omit <app_path> then current directory will be used.\n"
+ " If you omit <out_file> then <app_path>/dist/<name_of_original.apk>\n"
+ " will be used.\n"
+ "\n"
+ " OPTS:\n"
+ "\n"
+ " -f, --force-all\n"
+ " Skip changes detection and build all files.\n"
+ " -d, --debug\n"
+ " Build in debug mode. Check project page for more info.\n"
+ " -a, --aapt\n"
+ " Loads aapt from specified location.\n"
+ "\n"
+ " if|install-framework <framework.apk> [<tag>] --frame-path [<location>] \n"
+ " Install framework file to your system.\n"
+ "\n"
+ "For additional info, see: http://code.google.com/p/android-apktool/"
+ "\n"
+ "For smali/baksmali info, see: http://code.google.com/p/smali/");
}
Handler handler = new ConsoleHandler();
logger.addHandler(handler);
private static void setupLogging(Verbosity verbosity) {
Logger logger = Logger.getLogger("");
for (Handler handler : logger.getHandlers()) {
logger.removeHandler(handler);
}
if (verbosity == Verbosity.QUIET) {
return;
}
if (verbosity == Verbosity.VERBOSE) {
handler.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
} else {
handler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record) {
return record.getLevel().toString().charAt(0) + ": "
+ record.getMessage()
+ System.getProperty("line.separator");
}
});
}
}
Handler handler = new ConsoleHandler();
logger.addHandler(handler);
private static enum Verbosity {
NORMAL, VERBOSE, QUIET;
}
private static boolean Advanced = false;
if (verbosity == Verbosity.VERBOSE) {
handler.setLevel(Level.ALL);
logger.setLevel(Level.ALL);
} else {
handler.setFormatter(new Formatter() {
@Override
public String format(LogRecord record) {
return record.getLevel().toString().charAt(0) + ": "
+ record.getMessage()
+ System.getProperty("line.separator");
}
});
}
}
static class InvalidArgsError extends AndrolibException {
private static enum Verbosity {
NORMAL, VERBOSE, QUIET;
}
}
private static boolean Advanced = false;
static class InvalidArgsError extends AndrolibException {
}
}

View File

@ -21,16 +21,15 @@ import org.xmlpull.v1.XmlPullParser;
import android.util.AttributeSet;
/**
* The XML parsing interface returned for an XML resource. This is a standard
* XmlPullParser interface, as well as an extended AttributeSet interface and
* an additional close() method on this interface for the client to indicate
* when it is done reading the resource.
* The XML parsing interface returned for an XML resource. This is a standard
* XmlPullParser interface, as well as an extended AttributeSet interface and an
* additional close() method on this interface for the client to indicate when
* it is done reading the resource.
*/
public interface XmlResourceParser extends XmlPullParser, AttributeSet {
/**
* Close this interface to the resource. Calls on the interface are no
* longer value after this call.
*/
public void close();
/**
* Close this interface to the resource. Calls on the interface are no
* longer value after this call.
*/
public void close();
}

View File

@ -17,33 +17,61 @@ package android.util;
/**
* @author Dmitry Skiba
*
*
*/
public interface AttributeSet {
int getAttributeCount();
String getAttributeName(int index);
String getAttributeValue(int index);
String getPositionDescription();
int getAttributeNameResource(int index);
int getAttributeListValue(int index,String options[],int defaultValue);
boolean getAttributeBooleanValue(int index,boolean defaultValue);
int getAttributeResourceValue(int index,int defaultValue);
int getAttributeIntValue(int index,int defaultValue);
int getAttributeUnsignedIntValue(int index,int defaultValue);
float getAttributeFloatValue(int index,float defaultValue);
String getIdAttribute();
String getClassAttribute();
int getIdAttributeResourceValue(int index);
int getStyleAttribute();
String getAttributeValue(String namespace, String attribute);
int getAttributeListValue(String namespace,String attribute,String options[],int defaultValue);
boolean getAttributeBooleanValue(String namespace,String attribute,boolean defaultValue);
int getAttributeResourceValue(String namespace,String attribute,int defaultValue);
int getAttributeIntValue(String namespace,String attribute,int defaultValue);
int getAttributeUnsignedIntValue(String namespace,String attribute,int defaultValue);
float getAttributeFloatValue(String namespace,String attribute,float defaultValue);
int getAttributeCount();
//TODO: remove
int getAttributeValueType(int index);
int getAttributeValueData(int index);
String getAttributeName(int index);
String getAttributeValue(int index);
String getPositionDescription();
int getAttributeNameResource(int index);
int getAttributeListValue(int index, String options[], int defaultValue);
boolean getAttributeBooleanValue(int index, boolean defaultValue);
int getAttributeResourceValue(int index, int defaultValue);
int getAttributeIntValue(int index, int defaultValue);
int getAttributeUnsignedIntValue(int index, int defaultValue);
float getAttributeFloatValue(int index, float defaultValue);
String getIdAttribute();
String getClassAttribute();
int getIdAttributeResourceValue(int index);
int getStyleAttribute();
String getAttributeValue(String namespace, String attribute);
int getAttributeListValue(String namespace, String attribute,
String options[], int defaultValue);
boolean getAttributeBooleanValue(String namespace, String attribute,
boolean defaultValue);
int getAttributeResourceValue(String namespace, String attribute,
int defaultValue);
int getAttributeIntValue(String namespace, String attribute,
int defaultValue);
int getAttributeUnsignedIntValue(String namespace, String attribute,
int defaultValue);
float getAttributeFloatValue(String namespace, String attribute,
float defaultValue);
// TODO: remove
int getAttributeValueType(int index);
int getAttributeValueData(int index);
}

View File

@ -17,247 +17,285 @@
package android.util;
/**
* Container for a dynamically typed data value. Primarily used with
* 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 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;
/** 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;
/**
* 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;
/**
* 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;
/**
* 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;
/**
* 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 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;
/** 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;
/** 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_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;
/**
* {@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: 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: 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;
/** 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 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;
/**
* 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;
/**
* 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
};
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];
}
/**
* 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"
};
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";
}
/**
* 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) {
String res =String.format("%08x", data);
char[] vals = res.toCharArray();
switch (type) {
default:
case TYPE_INT_COLOR_ARGB8://#AaRrGgBb
break;
case TYPE_INT_COLOR_RGB8://#FFRrGgBb->#RrGgBb
res = res.substring(2);
break;
case TYPE_INT_COLOR_ARGB4://#AARRGGBB->#ARGB
res = new StringBuffer().append(vals[0]).append(vals[2]).append(vals[4]).append(vals[6]).toString();
break;
case TYPE_INT_COLOR_RGB4://#FFRRGGBB->#RGB
res = new StringBuffer().append(vals[2]).append(vals[4]).append(vals[6]).toString();
break;
}
return "#" + res;
} else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
String res;
switch (type) {
default:
case TYPE_INT_DEC:
res = Integer.toString(data);
break;
//defined before
/*case TYPE_INT_HEX:
res = "0x" + Integer.toHexString(data);
break;
case TYPE_INT_BOOLEAN:
res = (data != 0) ? "true":"false";
break;*/
}
return res;
}
if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
String res = String.format("%08x", data);
char[] vals = res.toCharArray();
switch (type) {
default:
case TYPE_INT_COLOR_ARGB8:// #AaRrGgBb
break;
case TYPE_INT_COLOR_RGB8:// #FFRrGgBb->#RrGgBb
res = res.substring(2);
break;
case TYPE_INT_COLOR_ARGB4:// #AARRGGBB->#ARGB
res = new StringBuffer().append(vals[0]).append(vals[2])
.append(vals[4]).append(vals[6]).toString();
break;
case TYPE_INT_COLOR_RGB4:// #FFRRGGBB->#RGB
res = new StringBuffer().append(vals[2]).append(vals[4])
.append(vals[6]).toString();
break;
}
return "#" + res;
} else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
String res;
switch (type) {
default:
case TYPE_INT_DEC:
res = Integer.toString(data);
break;
// defined before
/*
* case TYPE_INT_HEX: res = "0x" + Integer.toHexString(data); break;
* case TYPE_INT_BOOLEAN: res = (data != 0) ? "true":"false"; break;
*/
}
return res;
}
return null;
}
return null;
}
};

View File

@ -22,18 +22,18 @@ import brut.common.BrutException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class AndrolibException extends BrutException {
public AndrolibException() {
}
public AndrolibException() {
}
public AndrolibException(String message) {
super(message);
}
public AndrolibException(String message) {
super(message);
}
public AndrolibException(String message, Throwable cause) {
super(message, cause);
}
public AndrolibException(String message, Throwable cause) {
super(message, cause);
}
public AndrolibException(Throwable cause) {
super(cause);
}
public AndrolibException(Throwable cause) {
super(cause);
}
}

View File

@ -95,17 +95,19 @@ public class ApkDecoder {
}
if (hasResources()) {
// read the resources.arsc checking for STORED vs DEFLATE compression
// read the resources.arsc checking for STORED vs DEFLATE
// compression
// this will determine whether we compress on rebuild or not.
JarFile jf = new JarFile(mApkFile.getAbsoluteFile());
JarEntry je = jf.getJarEntry("resources.arsc");
if (je != null) {
int compression = je.getMethod();
mCompressResources = (compression != ZipEntry.STORED) && (compression == ZipEntry.DEFLATED);
JarEntry je = jf.getJarEntry("resources.arsc");
if (je != null) {
int compression = je.getMethod();
mCompressResources = (compression != ZipEntry.STORED)
&& (compression == ZipEntry.DEFLATED);
}
jf.close();
switch (mDecodeResources) {
case DECODE_RESOURCES_NONE:
mAndrolib.decodeResourcesRaw(mApkFile, outDir);
@ -286,12 +288,12 @@ public class ApkDecoder {
meta.put("packageInfo", info);
}
}
private void putCompressionInfo(Map<String, Object> meta)
throws AndrolibException {
meta.put("compressionType", getCompressionType());
}
private boolean getCompressionType() {
return mCompressResources;
}

View File

@ -20,56 +20,57 @@ import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import java.util.logging.Logger;
import org.jf.baksmali.baksmali;
import org.jf.smali.main;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ApktoolProperties {
public static String get(String key) {
return get().getProperty(key);
}
public static Properties get() {
if (sProps == null) {
loadProps();
}
return sProps;
}
public static String get(String key) {
return get().getProperty(key);
}
private static void loadProps() {
InputStream in = ApktoolProperties.class.getResourceAsStream("/properties/apktool.properties");
sProps = new Properties();
try {
sProps.load(in);
in.close();
} catch (IOException ex) {
LOGGER.warning("Can't load properties.");
}
public static Properties get() {
if (sProps == null) {
loadProps();
}
return sProps;
}
InputStream templateStream = ApktoolProperties.class.getResourceAsStream("/properties/baksmali.properties");
Properties properties = new Properties();
String version = "(unknown)";
try {
properties.load(templateStream);
version = properties.getProperty("application.version");
} catch (IOException ex) {
}
sProps.put("baksmaliVersion", version);
templateStream = ApktoolProperties.class.getResourceAsStream("/properties/smali.properties");
properties = new Properties();
version = "(unknown)";
try {
properties.load(templateStream);
version = properties.getProperty("application.version");
} catch (IOException ex) {
}
sProps.put("smaliVersion", version);
}
private static Properties sProps;
private static final Logger LOGGER =
Logger.getLogger(ApktoolProperties.class.getName());
private static void loadProps() {
InputStream in = ApktoolProperties.class
.getResourceAsStream("/properties/apktool.properties");
sProps = new Properties();
try {
sProps.load(in);
in.close();
} catch (IOException ex) {
LOGGER.warning("Can't load properties.");
}
InputStream templateStream = ApktoolProperties.class
.getResourceAsStream("/properties/baksmali.properties");
Properties properties = new Properties();
String version = "(unknown)";
try {
properties.load(templateStream);
version = properties.getProperty("application.version");
} catch (IOException ex) {
}
sProps.put("baksmaliVersion", version);
templateStream = ApktoolProperties.class
.getResourceAsStream("/properties/smali.properties");
properties = new Properties();
version = "(unknown)";
try {
properties.load(templateStream);
version = properties.getProperty("application.version");
} catch (IOException ex) {
}
sProps.put("smaliVersion", version);
}
private static Properties sProps;
private static final Logger LOGGER = Logger
.getLogger(ApktoolProperties.class.getName());
}

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/
public class CantFind9PatchChunk extends AndrolibException {
public CantFind9PatchChunk(Throwable cause) {
super(cause);
}
public CantFind9PatchChunk(Throwable cause) {
super(cause);
}
public CantFind9PatchChunk(String message, Throwable cause) {
super(message, cause);
}
public CantFind9PatchChunk(String message, Throwable cause) {
super(message, cause);
}
public CantFind9PatchChunk(String message) {
super(message);
}
public CantFind9PatchChunk(String message) {
super(message);
}
public CantFind9PatchChunk() {
}
public CantFind9PatchChunk() {
}
}

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/
public class CantFindFrameworkResException extends AndrolibException {
public CantFindFrameworkResException(Throwable cause, int id) {
super(cause);
mPkgId = id;
}
public CantFindFrameworkResException(Throwable cause, int id) {
super(cause);
mPkgId = id;
}
public CantFindFrameworkResException(int id) {
mPkgId = id;
}
public CantFindFrameworkResException(int id) {
mPkgId = id;
}
public int getPkgId() {
return mPkgId;
}
public int getPkgId() {
return mPkgId;
}
private final int mPkgId;
private final int mPkgId;
}

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/
public class InFileNotFoundException extends AndrolibException {
public InFileNotFoundException(Throwable cause) {
super(cause);
}
public InFileNotFoundException(Throwable cause) {
super(cause);
}
public InFileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public InFileNotFoundException(String message, Throwable cause) {
super(message, cause);
}
public InFileNotFoundException(String message) {
super(message);
}
public InFileNotFoundException(String message) {
super(message);
}
public InFileNotFoundException() {
}
public InFileNotFoundException() {
}
}

View File

@ -23,18 +23,18 @@ import brut.androlib.AndrolibException;
*/
public class OutDirExistsException extends AndrolibException {
public OutDirExistsException(Throwable cause) {
super(cause);
}
public OutDirExistsException(Throwable cause) {
super(cause);
}
public OutDirExistsException(String message, Throwable cause) {
super(message, cause);
}
public OutDirExistsException(String message, Throwable cause) {
super(message, cause);
}
public OutDirExistsException(String message) {
super(message);
}
public OutDirExistsException(String message) {
super(message);
}
public OutDirExistsException() {
}
public OutDirExistsException() {
}
}

View File

@ -22,18 +22,18 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class UndefinedResObject extends AndrolibException {
public UndefinedResObject(Throwable cause) {
super(cause);
}
public UndefinedResObject(Throwable cause) {
super(cause);
}
public UndefinedResObject(String message, Throwable cause) {
super(message, cause);
}
public UndefinedResObject(String message, Throwable cause) {
super(message, cause);
}
public UndefinedResObject(String message) {
super(message);
}
public UndefinedResObject(String message) {
super(message);
}
public UndefinedResObject() {
}
public UndefinedResObject() {
}
}

View File

@ -24,11 +24,11 @@ import java.io.File;
*/
public class AndrolibJava {
public void decode(ExtFile apkFile, File outDir) {
throw new UnsupportedOperationException("Not yet implemented");
}
public void decode(ExtFile apkFile, File outDir) {
throw new UnsupportedOperationException("Not yet implemented");
}
public void build(File javaDir, File dex) {
throw new UnsupportedOperationException("Not yet implemented");
}
public void build(File javaDir, File dex) {
throw new UnsupportedOperationException("Not yet implemented");
}
}

View File

@ -23,7 +23,7 @@ import java.io.Writer;
*/
public class IndentingWriter extends org.jf.util.IndentingWriter {
public IndentingWriter(Writer writer) {
super(writer);
}
public IndentingWriter(Writer writer) {
super(writer);
}
}

View File

@ -28,57 +28,58 @@ import org.jf.smali.*;
*/
public class SmaliMod {
public static boolean assembleSmaliFile(InputStream smaliStream,
String name, DexFile dexFile, boolean verboseErrors,
boolean oldLexer, boolean printTokens)
throws IOException, RecognitionException {
CommonTokenStream tokens;
public static boolean assembleSmaliFile(InputStream smaliStream,
String name, DexFile dexFile, boolean verboseErrors,
boolean oldLexer, boolean printTokens) throws IOException,
RecognitionException {
CommonTokenStream tokens;
boolean lexerErrors = false;
LexerErrorInterface lexer;
boolean lexerErrors = false;
LexerErrorInterface lexer;
InputStreamReader reader = new InputStreamReader(smaliStream, "UTF-8");
InputStreamReader reader = new InputStreamReader(smaliStream, "UTF-8");
lexer = new smaliFlexLexer(reader);
tokens = new CommonTokenStream((TokenSource) lexer);
lexer = new smaliFlexLexer(reader);
tokens = new CommonTokenStream((TokenSource)lexer);
if (printTokens) {
tokens.getTokens();
if (printTokens) {
tokens.getTokens();
for (int i = 0; i < tokens.size(); i++) {
Token token = tokens.get(i);
if (token.getChannel() == BaseRecognizer.HIDDEN) {
continue;
}
for (int i=0; i<tokens.size(); i++) {
Token token = tokens.get(i);
if (token.getChannel() == smaliParser.HIDDEN) {
continue;
}
System.out.println(smaliParser.tokenNames[token.getType()]
+ ": " + token.getText());
}
}
System.out.println(smaliParser.tokenNames[token.getType()] + ": " + token.getText());
}
}
smaliParser parser = new smaliParser(tokens);
parser.setVerboseErrors(verboseErrors);
smaliParser parser = new smaliParser(tokens);
parser.setVerboseErrors(verboseErrors);
smaliParser.smali_file_return result = parser.smali_file();
smaliParser.smali_file_return result = parser.smali_file();
if (parser.getNumberOfSyntaxErrors() > 0
|| lexer.getNumberOfSyntaxErrors() > 0) {
return false;
}
if (parser.getNumberOfSyntaxErrors() > 0 || lexer.getNumberOfSyntaxErrors() > 0) {
return false;
}
CommonTree t = (CommonTree) result.getTree();
CommonTree t = (CommonTree) result.getTree();
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
treeStream.setTokenStream(tokens);
CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
treeStream.setTokenStream(tokens);
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
dexGen.dexFile = dexFile;
dexGen.smali_file();
dexGen.dexFile = dexFile;
dexGen.smali_file();
if (dexGen.getNumberOfSyntaxErrors() > 0) {
return false;
}
if (dexGen.getNumberOfSyntaxErrors() > 0) {
return false;
}
return true;
}
return true;
}
}

View File

@ -145,25 +145,27 @@ final public class AndrolibResources {
LOGGER.info("Decoding AndroidManifest.xml with only framework resources...");
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
"AndroidManifest.xml");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
public void adjust_package_manifest(ResTable resTable, String filePath)
public void adjust_package_manifest(ResTable resTable, String filePath)
throws AndrolibException {
// check if packages different, and that package is not equal to "android"
// check if packages different, and that package is not equal to
// "android"
Map<String, String> packageInfo = resTable.getPackageInfo();
if ((packageInfo.get("cur_package").equalsIgnoreCase(packageInfo.get("orig_package"))
|| ("android".equalsIgnoreCase(packageInfo.get("cur_package"))
|| ("com.htc".equalsIgnoreCase(packageInfo.get("cur_package")))))) {
LOGGER.info("Regular manifest package...");
} else {
if ((packageInfo.get("cur_package").equalsIgnoreCase(
packageInfo.get("orig_package")) || ("android"
.equalsIgnoreCase(packageInfo.get("cur_package")) || ("com.htc"
.equalsIgnoreCase(packageInfo.get("cur_package")))))) {
LOGGER.info("Regular manifest package...");
} else {
try {
LOGGER.info("Renamed manifest package found! Fixing...");
DocumentBuilderFactory docFactory = DocumentBuilderFactory
.newInstance();
@ -178,15 +180,16 @@ final public class AndrolibResources {
Node nodeAttr = attr.getNamedItem("package");
mPackageRenamed = nodeAttr.getNodeValue();
nodeAttr.setNodeValue(packageInfo.get("cur_package"));
// re-save manifest.
// fancy an auto-sort :p
TransformerFactory transformerFactory = TransformerFactory.newInstance();
TransformerFactory transformerFactory = TransformerFactory
.newInstance();
Transformer transformer = transformerFactory.newTransformer();
DOMSource source = new DOMSource(doc);
StreamResult result = new StreamResult(new File(filePath));
transformer.transform(source, result);
} catch (ParserConfigurationException ex) {
throw new AndrolibException(ex);
} catch (TransformerException ex) {
@ -217,9 +220,10 @@ final public class AndrolibResources {
fileDecoder.decodeManifest(inApk, "AndroidManifest.xml", out,
"AndroidManifest.xml");
// fix package if needed
adjust_package_manifest(resTable, outDir.getAbsolutePath() + "/AndroidManifest.xml");
adjust_package_manifest(resTable, outDir.getAbsolutePath()
+ "/AndroidManifest.xml");
if (inApk.containsDir("res")) {
in = inApk.getDir("res");
@ -268,19 +272,21 @@ final public class AndrolibResources {
public void aaptPackage(File apkFile, File manifest, File resDir,
File rawDir, File assetDir, File[] include,
HashMap<String, Boolean> flags, String aaptPath) throws AndrolibException {
HashMap<String, Boolean> flags, String aaptPath)
throws AndrolibException {
List<String> cmd = new ArrayList<String>();
// path for aapt binary
if (!aaptPath.isEmpty()) {
File aaptFile = new File(aaptPath);
if (aaptFile.canRead() && aaptFile.exists()) {
aaptFile.setExecutable(true);
cmd.add(aaptFile.getPath());
if (flags.get("verbose")) {
LOGGER.info(aaptFile.getPath() + " being used as aapt location.");
LOGGER.info(aaptFile.getPath()
+ " being used as aapt location.");
}
} else {
LOGGER.warning("aapt location could not be found. Defaulting back to default");
@ -323,10 +329,10 @@ final public class AndrolibResources {
if (flags.get("framework")) {
cmd.add("-x");
}
if (!(flags.get("compression"))) {
cmd.add("-0");
cmd.add("arsc");
cmd.add("-0");
cmd.add("arsc");
}
if (include != null) {
@ -420,7 +426,7 @@ final public class AndrolibResources {
" ");
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_LINE_SEPARATOR,
System.getProperty("line.separator"));
serial.setProperty(ExtMXSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
serial.setProperty(ExtXmlSerializer.PROPERTY_DEFAULT_ENCODING, "utf-8");
serial.setDisabledAttrEscape(true);
return serial;
}
@ -665,7 +671,8 @@ final public class AndrolibResources {
if (!dir.exists()) {
if (!dir.mkdirs()) {
if (sFrameworkFolder != null) {
System.out.println("Can't create Framework directory: " + dir);
System.out.println("Can't create Framework directory: "
+ dir);
}
throw new AndrolibException("Can't create directory: " + dir);
}
@ -681,7 +688,7 @@ final public class AndrolibResources {
throw new AndrolibException(ex);
}
}
public void setFrameworkFolder(String path) {
sFrameworkFolder = path;
}

View File

@ -36,132 +36,128 @@ 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 {
tagResIdsForFile(resTable, dir, fileName);
} 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 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 {
tagResIdsForFile(resTable, dir, fileName);
} 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);
}
}
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 void tagResIdsForFile(ResTable resTable, Directory dir,
String fileName) throws IOException, DirectoryException,
AndrolibException {
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) {
try {
ResResSpec spec = resTable.getResSpec(resID);
out.println(String.format(
RES_NAME_FORMAT, spec.getFullName()));
} catch (UndefinedResObject ex) {
if (! R_FILE_PATTERN.matcher(fileName).matches()) {
LOGGER.warning(String.format(
"Undefined resource spec in %s: 0x%08x"
, fileName, resID));
}
}
}
}
out.println(line);
}
out.close();
}
private void tagResIdsForFile(ResTable resTable, Directory dir,
String fileName) throws IOException, DirectoryException,
AndrolibException {
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) {
try {
ResResSpec spec = resTable.getResSpec(resID);
out.println(String.format(RES_NAME_FORMAT,
spec.getFullName()));
} catch (UndefinedResObject ex) {
if (!R_FILE_PATTERN.matcher(fileName).matches()) {
LOGGER.warning(String.format(
"Undefined resource spec in %s: 0x%08x",
fileName, resID));
}
}
}
}
out.println(line);
}
out.close();
}
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 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._]+)$");
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._]+)$");
private final static Pattern R_FILE_PATTERN = Pattern.compile(
".*R\\$[a-z]+\\.smali$");
private final static Pattern R_FILE_PATTERN = Pattern
.compile(".*R\\$[a-z]+\\.smali$");
private final static Logger LOGGER =
Logger.getLogger(ResSmaliUpdater.class.getName());
private final static Logger LOGGER = Logger.getLogger(ResSmaliUpdater.class
.getName());
}

View File

@ -24,51 +24,49 @@ 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>();
private final ResConfigFlags mFlags;
private final Map<ResResSpec, ResResource> mResources = new LinkedHashMap<ResResSpec, ResResource>();
public ResConfig(ResConfigFlags flags) {
this.mFlags = flags;
}
public ResConfig(ResConfigFlags flags) {
this.mFlags = flags;
}
public Set<ResResource> listResources() {
return new LinkedHashSet<ResResource>(mResources.values());
}
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 UndefinedResObject(String.format(
"resource: spec=%s, config=%s", spec, this));
}
return res;
}
public ResResource getResource(ResResSpec spec) throws AndrolibException {
ResResource res = mResources.get(spec);
if (res == null) {
throw new UndefinedResObject(String.format(
"resource: spec=%s, config=%s", spec, this));
}
return res;
}
public Set<ResResSpec> listResSpecs() {
return mResources.keySet();
}
public Set<ResResSpec> listResSpecs() {
return mResources.keySet();
}
public ResConfigFlags getFlags() {
return mFlags;
}
public ResConfigFlags getFlags() {
return mFlags;
}
public void addResource(ResResource res)
throws AndrolibException {
addResource(res, false);
}
public void addResource(ResResource res) throws AndrolibException {
addResource(res, false);
}
public void addResource(ResResource res, boolean overwrite)
throws AndrolibException {
ResResSpec spec = res.getResSpec();
if (mResources.put(spec, res) != null && ! overwrite) {
throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", spec, this));
}
}
public void addResource(ResResource res, boolean overwrite)
throws AndrolibException {
ResResSpec spec = res.getResSpec();
if (mResources.put(spec, res) != null && !overwrite) {
throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", spec, this));
}
}
@Override
public String toString() {
return mFlags.toString();
}
@Override
public String toString() {
return mFlags.toString();
}
}

View File

@ -22,457 +22,456 @@ import java.util.logging.Logger;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResConfigFlags {
public final short mcc;
public final short mnc;
public final short mcc;
public final short mnc;
public final char[] language;
public final char[] country;
public final short layoutDirection;
public final char[] language;
public final char[] country;
public final byte orientation;
public final byte touchscreen;
public final short density;
public final short layoutDirection;
public final byte keyboard;
public final byte navigation;
public final byte inputFlags;
public final byte orientation;
public final byte touchscreen;
public final short density;
public final short screenWidth;
public final short screenHeight;
public final byte keyboard;
public final byte navigation;
public final byte inputFlags;
public final short sdkVersion;
public final short screenWidth;
public final short screenHeight;
public final byte screenLayout;
public final byte uiMode;
public final short smallestScreenWidthDp;
public final short sdkVersion;
public final short screenWidthDp;
public final short screenHeightDp;
public final byte screenLayout;
public final byte uiMode;
public final short smallestScreenWidthDp;
public final boolean isInvalid;
public final short screenWidthDp;
public final short screenHeightDp;
private final String mQualifiers;
public final boolean isInvalid;
public ResConfigFlags() {
mcc = 0;
mnc = 0;
language = new char[]{'\00', '\00'};
country = new char[]{'\00', '\00'};
layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY;
orientation = ORIENTATION_ANY;
touchscreen = TOUCHSCREEN_ANY;
density = DENSITY_DEFAULT;
keyboard = KEYBOARD_ANY;
navigation = NAVIGATION_ANY;
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
screenWidth = 0;
screenHeight = 0;
sdkVersion = 0;
screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY;
smallestScreenWidthDp = 0;
screenWidthDp = 0;
screenHeightDp = 0;
isInvalid = false;
mQualifiers = "";
}
private final String mQualifiers;
public ResConfigFlags(short mcc, short mnc, char[] language, char[] country,
short layoutDirection, byte orientation, byte touchscreen,
short density, byte keyboard, byte navigation, byte inputFlags,
short screenWidth, short screenHeight, short sdkVersion, byte screenLayout,
byte uiMode, short smallestScreenWidthDp, short screenWidthDp,
short screenHeightDp, boolean isInvalid) {
if (orientation < 0 || orientation > 3) {
LOGGER.warning("Invalid orientation value: " + orientation);
orientation = 0;
isInvalid = true;
}
if (touchscreen < 0 || touchscreen > 3) {
LOGGER.warning("Invalid touchscreen value: " + touchscreen);
touchscreen = 0;
isInvalid = true;
}
if (density < -1) {
LOGGER.warning("Invalid density value: " + density);
density = 0;
isInvalid = true;
}
if (keyboard < 0 || keyboard > 3) {
LOGGER.warning("Invalid keyboard value: " + keyboard);
keyboard = 0;
isInvalid = true;
}
if (navigation < 0 || navigation > 4) {
LOGGER.warning("Invalid navigation value: " + navigation);
navigation = 0;
isInvalid = true;
}
public ResConfigFlags() {
mcc = 0;
mnc = 0;
language = new char[] { '\00', '\00' };
country = new char[] { '\00', '\00' };
layoutDirection = SCREENLAYOUT_LAYOUTDIR_ANY;
orientation = ORIENTATION_ANY;
touchscreen = TOUCHSCREEN_ANY;
density = DENSITY_DEFAULT;
keyboard = KEYBOARD_ANY;
navigation = NAVIGATION_ANY;
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
screenWidth = 0;
screenHeight = 0;
sdkVersion = 0;
screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
uiMode = UI_MODE_TYPE_ANY | UI_MODE_NIGHT_ANY;
smallestScreenWidthDp = 0;
screenWidthDp = 0;
screenHeightDp = 0;
isInvalid = false;
mQualifiers = "";
}
this.mcc = mcc;
this.mnc = mnc;
this.language = language;
this.country = country;
this.layoutDirection = layoutDirection;
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.sdkVersion = sdkVersion;
this.screenLayout = screenLayout;
this.uiMode = uiMode;
this.smallestScreenWidthDp = smallestScreenWidthDp;
this.screenWidthDp = screenWidthDp;
this.screenHeightDp = screenHeightDp;
this.isInvalid = isInvalid;
mQualifiers = generateQualifiers();
}
public String getQualifiers() {
return mQualifiers;
}
private String generateQualifiers() {
StringBuilder ret = new StringBuilder();
if (mcc != 0) {
ret.append("-mcc").append(String.format("%03d", 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_LAYOUTDIR) {
case SCREENLAYOUT_LAYOUTDIR_RTL:
ret.append("-ldrtl");
break;
case SCREENLAYOUT_LAYOUTDIR_LTR:
ret.append("-ldltr");
break;
public ResConfigFlags(short mcc, short mnc, char[] language,
char[] country, short layoutDirection, byte orientation,
byte touchscreen, short density, byte keyboard, byte navigation,
byte inputFlags, short screenWidth, short screenHeight,
short sdkVersion, byte screenLayout, byte uiMode,
short smallestScreenWidthDp, short screenWidthDp,
short screenHeightDp, boolean isInvalid) {
if (orientation < 0 || orientation > 3) {
LOGGER.warning("Invalid orientation value: " + orientation);
orientation = 0;
isInvalid = true;
}
if (touchscreen < 0 || touchscreen > 3) {
LOGGER.warning("Invalid touchscreen value: " + touchscreen);
touchscreen = 0;
isInvalid = true;
}
if (density < -1) {
LOGGER.warning("Invalid density value: " + density);
density = 0;
isInvalid = true;
}
if (keyboard < 0 || keyboard > 3) {
LOGGER.warning("Invalid keyboard value: " + keyboard);
keyboard = 0;
isInvalid = true;
}
if (navigation < 0 || navigation > 4) {
LOGGER.warning("Invalid navigation value: " + navigation);
navigation = 0;
isInvalid = true;
}
if (smallestScreenWidthDp != 0) {
ret.append("-sw").append(smallestScreenWidthDp).append("dp");
}
if (screenWidthDp != 0) {
ret.append("-w").append(screenWidthDp).append("dp");
}
if (screenHeightDp != 0) {
ret.append("-h").append(screenHeightDp).append("dp");
}
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;
case SCREENSIZE_XLARGE:
ret.append("-xlarge");
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 (uiMode & MASK_UI_MODE_TYPE) {
case UI_MODE_TYPE_CAR:
ret.append("-car");
break;
case UI_MODE_TYPE_DESK:
ret.append("-desk");
break;
case UI_MODE_TYPE_TELEVISION:
ret.append("-television");
break;
case UI_MODE_TYPE_SMALLUI:
ret.append("-smallui");
break;
case UI_MODE_TYPE_MEDIUMUI:
ret.append("-mediumui");
break;
case UI_MODE_TYPE_LARGEUI:
ret.append("-largeui");
break;
case UI_MODE_TYPE_HUGEUI:
ret.append("-hugeui");
break;
case UI_MODE_TYPE_APPLIANCE:
ret.append("-appliance");
break;
}
switch (uiMode & MASK_UI_MODE_NIGHT) {
case UI_MODE_NIGHT_YES:
ret.append("-night");
break;
case UI_MODE_NIGHT_NO:
ret.append("-notnight");
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_TV:
ret.append("-tvdpi");
break;
case DENSITY_XHIGH:
ret.append("-xhdpi");
break;
case DENSITY_XXHIGH:
ret.append("-xxhdpi");
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 > getNaturalSdkVersionRequirement()) {
ret.append("-v").append(sdkVersion);
}
if (isInvalid) {
ret.append("-ERR" + sErrCounter++);
}
return ret.toString();
}
this.mcc = mcc;
this.mnc = mnc;
this.language = language;
this.country = country;
this.layoutDirection = layoutDirection;
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.sdkVersion = sdkVersion;
this.screenLayout = screenLayout;
this.uiMode = uiMode;
this.smallestScreenWidthDp = smallestScreenWidthDp;
this.screenWidthDp = screenWidthDp;
this.screenHeightDp = screenHeightDp;
this.isInvalid = isInvalid;
mQualifiers = generateQualifiers();
}
private short getNaturalSdkVersionRequirement() {
if (smallestScreenWidthDp != 0 || screenWidthDp != 0
|| screenHeightDp != 0) {
return SDK_HONEYCOMB_MR2;
}
if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) {
return SDK_FROYO;
}
if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0
|| density != DENSITY_DEFAULT) {
return SDK_DONUT;
}
return 0;
}
public String getQualifiers() {
return mQualifiers;
}
@Override
public String toString() {
return ! getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]";
}
private String generateQualifiers() {
StringBuilder ret = new StringBuilder();
if (mcc != 0) {
ret.append("-mcc").append(String.format("%03d", 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_LAYOUTDIR) {
case SCREENLAYOUT_LAYOUTDIR_RTL:
ret.append("-ldrtl");
break;
case SCREENLAYOUT_LAYOUTDIR_LTR:
ret.append("-ldltr");
break;
}
if (smallestScreenWidthDp != 0) {
ret.append("-sw").append(smallestScreenWidthDp).append("dp");
}
if (screenWidthDp != 0) {
ret.append("-w").append(screenWidthDp).append("dp");
}
if (screenHeightDp != 0) {
ret.append("-h").append(screenHeightDp).append("dp");
}
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;
case SCREENSIZE_XLARGE:
ret.append("-xlarge");
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 (uiMode & MASK_UI_MODE_TYPE) {
case UI_MODE_TYPE_CAR:
ret.append("-car");
break;
case UI_MODE_TYPE_DESK:
ret.append("-desk");
break;
case UI_MODE_TYPE_TELEVISION:
ret.append("-television");
break;
case UI_MODE_TYPE_SMALLUI:
ret.append("-smallui");
break;
case UI_MODE_TYPE_MEDIUMUI:
ret.append("-mediumui");
break;
case UI_MODE_TYPE_LARGEUI:
ret.append("-largeui");
break;
case UI_MODE_TYPE_HUGEUI:
ret.append("-hugeui");
break;
case UI_MODE_TYPE_APPLIANCE:
ret.append("-appliance");
break;
}
switch (uiMode & MASK_UI_MODE_NIGHT) {
case UI_MODE_NIGHT_YES:
ret.append("-night");
break;
case UI_MODE_NIGHT_NO:
ret.append("-notnight");
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_TV:
ret.append("-tvdpi");
break;
case DENSITY_XHIGH:
ret.append("-xhdpi");
break;
case DENSITY_XXHIGH:
ret.append("-xxhdpi");
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 > getNaturalSdkVersionRequirement()) {
ret.append("-v").append(sdkVersion);
}
if (isInvalid) {
ret.append("-ERR" + sErrCounter++);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResConfigFlags other = (ResConfigFlags) obj;
return this.mQualifiers.equals(other.mQualifiers);
}
return ret.toString();
}
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + this.mQualifiers.hashCode();
return hash;
}
private short getNaturalSdkVersionRequirement() {
if (smallestScreenWidthDp != 0 || screenWidthDp != 0
|| screenHeightDp != 0) {
return SDK_HONEYCOMB_MR2;
}
if ((uiMode & (MASK_UI_MODE_TYPE | MASK_UI_MODE_NIGHT)) != 0) {
return SDK_FROYO;
}
if ((screenLayout & (MASK_SCREENSIZE | MASK_SCREENLONG)) != 0
|| density != DENSITY_DEFAULT) {
return SDK_DONUT;
}
return 0;
}
@Override
public String toString() {
return !getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]";
}
// TODO: Dirty static hack. This counter should be a part of ResPackage,
// but it would be hard right now and this feature is very rarely used.
private static int sErrCounter = 0;
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResConfigFlags other = (ResConfigFlags) obj;
return this.mQualifiers.equals(other.mQualifiers);
}
public final static byte SDK_BASE = 1;
public final static byte SDK_BASE_1_1 = 2;
public final static byte SDK_CUPCAKE = 3;
public final static byte SDK_DONUT = 4;
public final static byte SDK_ECLAIR = 5;
public final static byte SDK_ECLAIR_0_1 = 6;
public final static byte SDK_ECLAIR_MR1 = 7;
public final static byte SDK_FROYO = 8;
public final static byte SDK_GINGERBREAD = 9;
public final static byte SDK_GINGERBREAD_MR1 = 10;
public final static byte SDK_HONEYCOMB = 11;
public final static byte SDK_HONEYCOMB_MR1 = 12;
public final static byte SDK_HONEYCOMB_MR2 = 13;
public final static byte SDK_ICE_CREAM_SANDWICH = 14;
public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15;
public final static byte SDK_JELLY_BEAN = 16;
public final static byte SDK_JELLY_BEAN_MR1 = 17;
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + this.mQualifiers.hashCode();
return hash;
}
public final static byte ORIENTATION_ANY = 0;
public final static byte ORIENTATION_PORT = 1;
public final static byte ORIENTATION_LAND = 2;
public final static byte ORIENTATION_SQUARE = 3;
// TODO: Dirty static hack. This counter should be a part of ResPackage,
// but it would be hard right now and this feature is very rarely used.
private static int sErrCounter = 0;
public final static byte TOUCHSCREEN_ANY = 0;
public final static byte TOUCHSCREEN_NOTOUCH = 1;
public final static byte TOUCHSCREEN_STYLUS = 2;
public final static byte TOUCHSCREEN_FINGER = 3;
public final static byte SDK_BASE = 1;
public final static byte SDK_BASE_1_1 = 2;
public final static byte SDK_CUPCAKE = 3;
public final static byte SDK_DONUT = 4;
public final static byte SDK_ECLAIR = 5;
public final static byte SDK_ECLAIR_0_1 = 6;
public final static byte SDK_ECLAIR_MR1 = 7;
public final static byte SDK_FROYO = 8;
public final static byte SDK_GINGERBREAD = 9;
public final static byte SDK_GINGERBREAD_MR1 = 10;
public final static byte SDK_HONEYCOMB = 11;
public final static byte SDK_HONEYCOMB_MR1 = 12;
public final static byte SDK_HONEYCOMB_MR2 = 13;
public final static byte SDK_ICE_CREAM_SANDWICH = 14;
public final static byte SDK_ICE_CREAM_SANDWICH_MR1 = 15;
public final static byte SDK_JELLY_BEAN = 16;
public final static byte SDK_JELLY_BEAN_MR1 = 17;
public final static short DENSITY_DEFAULT = 0;
public final static short DENSITY_LOW = 120;
public final static short DENSITY_MEDIUM = 160;
public final static short DENSITY_TV = 213;
public final static short DENSITY_HIGH = 240;
public final static short DENSITY_XHIGH = 320;
public final static short DENSITY_XXHIGH = 480;
public final static short DENSITY_NONE = -1;
public final static short MASK_LAYOUTDIR = 0xc0;
public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00;
public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06;
public final static byte ORIENTATION_ANY = 0;
public final static byte ORIENTATION_PORT = 1;
public final static byte ORIENTATION_LAND = 2;
public final static byte ORIENTATION_SQUARE = 3;
public final static byte KEYBOARD_ANY = 0;
public final static byte KEYBOARD_NOKEYS = 1;
public final static byte KEYBOARD_QWERTY = 2;
public final static byte KEYBOARD_12KEY = 3;
public final static byte TOUCHSCREEN_ANY = 0;
public final static byte TOUCHSCREEN_NOTOUCH = 1;
public final static byte TOUCHSCREEN_STYLUS = 2;
public final static byte TOUCHSCREEN_FINGER = 3;
public final static byte NAVIGATION_ANY = 0;
public final static byte NAVIGATION_NONAV = 1;
public final static byte NAVIGATION_DPAD = 2;
public final static byte NAVIGATION_TRACKBALL = 3;
public final static byte NAVIGATION_WHEEL = 4;
public final static short DENSITY_DEFAULT = 0;
public final static short DENSITY_LOW = 120;
public final static short DENSITY_MEDIUM = 160;
public final static short DENSITY_TV = 213;
public final static short DENSITY_HIGH = 240;
public final static short DENSITY_XHIGH = 320;
public final static short DENSITY_XXHIGH = 480;
public final static short DENSITY_NONE = -1;
public final static byte MASK_KEYSHIDDEN = 0x3;
public final static byte KEYSHIDDEN_ANY = 0x0;
public final static byte KEYSHIDDEN_NO = 0x1;
public final static byte KEYSHIDDEN_YES = 0x2;
public final static byte KEYSHIDDEN_SOFT = 0x3;
public final static short MASK_LAYOUTDIR = 0xc0;
public final static short SCREENLAYOUT_LAYOUTDIR_ANY = 0x00;
public final static short SCREENLAYOUT_LAYOUTDIR_LTR = 0x40;
public final static short SCREENLAYOUT_LAYOUTDIR_RTL = 0x80;
public final static short SCREENLAYOUT_LAYOUTDIR_SHIFT = 0x06;
public final static byte MASK_NAVHIDDEN = 0xc;
public final static byte NAVHIDDEN_ANY = 0x0;
public final static byte NAVHIDDEN_NO = 0x4;
public final static byte NAVHIDDEN_YES = 0x8;
public final static byte KEYBOARD_ANY = 0;
public final static byte KEYBOARD_NOKEYS = 1;
public final static byte KEYBOARD_QWERTY = 2;
public final static byte KEYBOARD_12KEY = 3;
public final static byte MASK_SCREENSIZE = 0x0f;
public final static byte SCREENSIZE_ANY = 0x00;
public final static byte SCREENSIZE_SMALL = 0x01;
public final static byte SCREENSIZE_NORMAL = 0x02;
public final static byte SCREENSIZE_LARGE = 0x03;
public final static byte SCREENSIZE_XLARGE = 0x04;
public final static byte MASK_SCREENLONG = 0x30;
public final static byte SCREENLONG_ANY = 0x00;
public final static byte SCREENLONG_NO = 0x10;
public final static byte SCREENLONG_YES = 0x20;
public final static byte NAVIGATION_ANY = 0;
public final static byte NAVIGATION_NONAV = 1;
public final static byte NAVIGATION_DPAD = 2;
public final static byte NAVIGATION_TRACKBALL = 3;
public final static byte NAVIGATION_WHEEL = 4;
public final static byte MASK_UI_MODE_TYPE = 0x0f;
public final static byte UI_MODE_TYPE_ANY = 0x00;
public final static byte UI_MODE_TYPE_NORMAL = 0x01;
public final static byte UI_MODE_TYPE_DESK = 0x02;
public final static byte UI_MODE_TYPE_CAR = 0x03;
public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
public final static byte UI_MODE_TYPE_APPLIANCE = 0x05;
public final static byte UI_MODE_TYPE_SMALLUI = 0x0c;
public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d;
public final static byte UI_MODE_TYPE_LARGEUI = 0x0e;
public final static byte UI_MODE_TYPE_HUGEUI = 0x0f;
public final static byte MASK_KEYSHIDDEN = 0x3;
public final static byte KEYSHIDDEN_ANY = 0x0;
public final static byte KEYSHIDDEN_NO = 0x1;
public final static byte KEYSHIDDEN_YES = 0x2;
public final static byte KEYSHIDDEN_SOFT = 0x3;
public final static byte MASK_UI_MODE_NIGHT = 0x30;
public final static byte UI_MODE_NIGHT_ANY = 0x00;
public final static byte UI_MODE_NIGHT_NO = 0x10;
public final static byte UI_MODE_NIGHT_YES = 0x20;
public final static byte MASK_NAVHIDDEN = 0xc;
public final static byte NAVHIDDEN_ANY = 0x0;
public final static byte NAVHIDDEN_NO = 0x4;
public final static byte NAVHIDDEN_YES = 0x8;
public final static byte MASK_SCREENSIZE = 0x0f;
public final static byte SCREENSIZE_ANY = 0x00;
public final static byte SCREENSIZE_SMALL = 0x01;
public final static byte SCREENSIZE_NORMAL = 0x02;
public final static byte SCREENSIZE_LARGE = 0x03;
public final static byte SCREENSIZE_XLARGE = 0x04;
private static final Logger LOGGER =
Logger.getLogger(ResConfigFlags.class.getName());
public final static byte MASK_SCREENLONG = 0x30;
public final static byte SCREENLONG_ANY = 0x00;
public final static byte SCREENLONG_NO = 0x10;
public final static byte SCREENLONG_YES = 0x20;
public final static byte MASK_UI_MODE_TYPE = 0x0f;
public final static byte UI_MODE_TYPE_ANY = 0x00;
public final static byte UI_MODE_TYPE_NORMAL = 0x01;
public final static byte UI_MODE_TYPE_DESK = 0x02;
public final static byte UI_MODE_TYPE_CAR = 0x03;
public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
public final static byte UI_MODE_TYPE_APPLIANCE = 0x05;
public final static byte UI_MODE_TYPE_SMALLUI = 0x0c;
public final static byte UI_MODE_TYPE_MEDIUMUI = 0x0d;
public final static byte UI_MODE_TYPE_LARGEUI = 0x0e;
public final static byte UI_MODE_TYPE_HUGEUI = 0x0f;
public final static byte MASK_UI_MODE_NIGHT = 0x30;
public final static byte UI_MODE_NIGHT_ANY = 0x00;
public final static byte UI_MODE_NIGHT_NO = 0x10;
public final static byte UI_MODE_NIGHT_YES = 0x20;
private static final Logger LOGGER = Logger.getLogger(ResConfigFlags.class
.getName());
}

View File

@ -20,51 +20,51 @@ 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 package_;
public final int type;
public final int entry;
public final int id;
public final int id;
public ResID(int package_, int type, int entry) {
this(package_, type, entry, (package_ << 24) + (type << 16) + entry);
}
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 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;
}
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 String toString() {
return String.format("0x%08x", id);
}
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + this.id;
return hash;
}
@Override
public int hashCode() {
int hash = 17;
hash = 31 * 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;
}
@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

@ -28,193 +28,192 @@ 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<ResID> mSynthesizedRes = new HashSet<ResID>();
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<ResID> mSynthesizedRes = new HashSet<ResID>();
private ResValueFactory mValueFactory;
private ResValueFactory mValueFactory;
public ResPackage(ResTable resTable, int id, String name) {
this.mResTable = resTable;
this.mId = id;
this.mName = name;
}
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 List<ResResSpec> listResSpecs() {
return new ArrayList<ResResSpec>(mResSpecs.values());
}
public boolean hasResSpec(ResID resID) {
return mResSpecs.containsKey(resID);
}
public boolean hasResSpec(ResID resID) {
return mResSpecs.containsKey(resID);
}
public ResResSpec getResSpec(ResID resID) throws UndefinedResObject {
ResResSpec spec = mResSpecs.get(resID);
if (spec == null) {
throw new UndefinedResObject("resource spec: " + resID.toString());
}
return spec;
}
public ResResSpec getResSpec(ResID resID) throws UndefinedResObject {
ResResSpec spec = mResSpecs.get(resID);
if (spec == null) {
throw new UndefinedResObject("resource spec: " + resID.toString());
}
return spec;
}
public List<ResConfig> getConfigs() {
return new ArrayList<ResConfig>(mConfigs.values());
}
public List<ResConfig> getConfigs() {
return new ArrayList<ResConfig>(mConfigs.values());
}
public boolean hasConfig(ResConfigFlags flags) {
return mConfigs.containsKey(flags);
}
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 UndefinedResObject("config: " + flags);
}
return config;
}
public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException {
ResConfig config = mConfigs.get(flags);
if (config == null) {
throw new UndefinedResObject("config: " + flags);
}
return config;
}
public ResConfig getOrCreateConfig(ResConfigFlags flags)
throws AndrolibException {
ResConfig config = mConfigs.get(flags);
if (config == null) {
config = new ResConfig(flags);
mConfigs.put(flags, config);
}
return config;
}
public ResConfig getOrCreateConfig(ResConfigFlags flags)
throws AndrolibException {
ResConfig config = mConfigs.get(flags);
if (config == null) {
config = new ResConfig(flags);
mConfigs.put(flags, config);
}
return config;
}
public List<ResType> listTypes() {
return new ArrayList<ResType>(mTypes.values());
}
public List<ResType> listTypes() {
return new ArrayList<ResType>(mTypes.values());
}
public boolean hasType(String typeName) {
return mTypes.containsKey(typeName);
}
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 UndefinedResObject("type: " + typeName);
}
return type;
}
public ResType getType(String typeName) throws AndrolibException {
ResType type = mTypes.get(typeName);
if (type == null) {
throw new UndefinedResObject("type: " + typeName);
}
return type;
}
public Set<ResResource> listFiles() {
Set<ResResource> ret = new HashSet<ResResource>();
for (ResResSpec spec : mResSpecs.values()) {
for (ResResource res : spec.listResources()) {
if (res.getValue() instanceof ResFileValue) {
ret.add(res);
}
}
}
return ret;
}
public Set<ResResource> listFiles() {
Set<ResResource> ret = new HashSet<ResResource>();
for (ResResSpec spec : mResSpecs.values()) {
for (ResResource res : spec.listResources()) {
if (res.getValue() instanceof ResFileValue) {
ret.add(res);
}
}
}
return ret;
}
public Collection<ResValuesFile> listValuesFiles() {
Map<Duo<ResType, ResConfig>, ResValuesFile> ret =
new HashMap<Duo<ResType, ResConfig>, ResValuesFile>();
for (ResResSpec spec : mResSpecs.values()) {
for (ResResource res : spec.listResources()) {
if (res.getValue() instanceof ResValuesXmlSerializable) {
ResType type = res.getResSpec().getType();
ResConfig config = res.getConfig();
Duo<ResType, ResConfig> key =
new Duo<ResType, ResConfig>(type, config);
ResValuesFile values = ret.get(key);
if (values == null) {
values = new ResValuesFile(this, type, config);
ret.put(key, values);
}
values.addResource(res);
}
}
}
return ret.values();
}
public Collection<ResValuesFile> listValuesFiles() {
Map<Duo<ResType, ResConfig>, ResValuesFile> ret = new HashMap<Duo<ResType, ResConfig>, ResValuesFile>();
for (ResResSpec spec : mResSpecs.values()) {
for (ResResource res : spec.listResources()) {
if (res.getValue() instanceof ResValuesXmlSerializable) {
ResType type = res.getResSpec().getType();
ResConfig config = res.getConfig();
Duo<ResType, ResConfig> key = new Duo<ResType, ResConfig>(
type, config);
ResValuesFile values = ret.get(key);
if (values == null) {
values = new ResValuesFile(this, type, config);
ret.put(key, values);
}
values.addResource(res);
}
}
}
return ret.values();
}
public ResTable getResTable() {
return mResTable;
}
public ResTable getResTable() {
return mResTable;
}
public int getId() {
return mId;
}
public int getId() {
return mId;
}
public String getName() {
return mName;
}
public String getName() {
return mName;
}
boolean isSynthesized(ResID resId) {
return mSynthesizedRes.contains(resId);
}
boolean isSynthesized(ResID resId) {
return mSynthesizedRes.contains(resId);
}
public void addResSpec(ResResSpec spec) throws AndrolibException {
if (mResSpecs.put(spec.getId(), spec) != null) {
throw new AndrolibException("Multiple resource specs: " + spec);
}
}
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 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 addType(ResType type) throws AndrolibException {
if (mTypes.put(type.getName(), type) != null) {
throw new AndrolibException("Multiple types: " + type);
}
}
public void addResource(ResResource res) {
}
public void addResource(ResResource res) {
}
public void addSynthesizedRes(int resId) {
mSynthesizedRes.add(new ResID(resId));
}
public void addSynthesizedRes(int resId) {
mSynthesizedRes.add(new ResID(resId));
}
@Override
public String toString() {
return mName;
}
@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 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 = 17;
hash = 31 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0);
hash = 31 * hash + this.mId;
return hash;
}
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash
+ (this.mResTable != null ? this.mResTable.hashCode() : 0);
hash = 31 * hash + this.mId;
return hash;
}
public ResValueFactory getValueFactory() {
if (mValueFactory == null) {
mValueFactory = new ResValueFactory(this);
}
return mValueFactory;
}
public ResValueFactory getValueFactory() {
if (mValueFactory == null) {
mValueFactory = new ResValueFactory(this);
}
return mValueFactory;
}
}

View File

@ -25,101 +25,97 @@ import org.apache.commons.lang3.StringUtils;
* @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>();
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 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 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 UndefinedResObject(String.format(
"resource: spec=%s, config=%s", this, config));
}
return res;
}
public ResResource getResource(ResConfig config) throws AndrolibException {
return getResource(config.getFlags());
}
public boolean hasResource(ResConfig config) {
return hasResource(config.getFlags());
}
public ResResource getResource(ResConfigFlags config)
throws AndrolibException {
ResResource res = mResources.get(config);
if (res == null) {
throw new UndefinedResObject(String.format(
"resource: spec=%s, config=%s", this, config));
}
return res;
}
private boolean hasResource(ResConfigFlags flags) {
return mResources.containsKey(flags);
}
public boolean hasResource(ResConfig config) {
return hasResource(config.getFlags());
}
public ResResource getDefaultResource() throws AndrolibException {
return getResource(new ResConfigFlags());
}
private boolean hasResource(ResConfigFlags flags) {
return mResources.containsKey(flags);
}
public boolean hasDefaultResource() {
return mResources.containsKey(new ResConfigFlags());
}
public ResResource getDefaultResource() throws AndrolibException {
return getResource(new ResConfigFlags());
}
public String getFullName() {
return getFullName(false, false);
}
public boolean hasDefaultResource() {
return mResources.containsKey(new ResConfigFlags());
}
public String getFullName(ResPackage relativeToPackage,
boolean excludeType) {
return getFullName(
getPackage().equals(relativeToPackage), excludeType);
}
public String getFullName() {
return getFullName(false, false);
}
public String getFullName(boolean excludePackage, boolean excludeType) {
return
(excludePackage ? "" : getPackage().getName() + ":") +
(excludeType ? "" : getType().getName() + "/") + getName();
}
public String getFullName(ResPackage relativeToPackage, boolean excludeType) {
return getFullName(getPackage().equals(relativeToPackage), excludeType);
}
public ResID getId() {
return mId;
}
public String getFullName(boolean excludePackage, boolean excludeType) {
return (excludePackage ? "" : getPackage().getName() + ":")
+ (excludeType ? "" : getType().getName() + "/") + getName();
}
public String getName() {
return StringUtils.replace(mName, "\"", "q");
}
public ResID getId() {
return mId;
}
public ResPackage getPackage() {
return mPackage;
}
public String getName() {
return StringUtils.replace(mName, "\"", "q");
}
public ResType getType() {
return mType;
}
public ResPackage getPackage() {
return mPackage;
}
public void addResource(ResResource res)
throws AndrolibException {
addResource(res, false);
}
public ResType getType() {
return mType;
}
public void addResource(ResResource res, boolean overwrite)
throws AndrolibException {
ResConfigFlags flags = res.getConfig().getFlags();
if (mResources.put(flags, res) != null && ! overwrite) {
throw new AndrolibException(String.format("Multiple resources: spec=%s, config=%s", this, flags));
}
}
public void addResource(ResResource res) throws AndrolibException {
addResource(res, false);
}
@Override
public String toString() {
return mId.toString() + " " + mType.toString() + "/" + mName;
}
public void addResource(ResResource res, boolean overwrite)
throws AndrolibException {
ResConfigFlags flags = res.getConfig().getFlags();
if (mResources.put(flags, res) != null && !overwrite) {
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

@ -23,42 +23,41 @@ 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;
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 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 String getFilePath() {
return mResSpec.getType().getName()
+ mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
}
public ResConfig getConfig() {
return mConfig;
}
public ResConfig getConfig() {
return mConfig;
}
public ResResSpec getResSpec() {
return mResSpec;
}
public ResResSpec getResSpec() {
return mResSpec;
}
public ResValue getValue() {
return mValue;
}
public ResValue getValue() {
return mValue;
}
public void replace(ResValue value) throws AndrolibException {
ResResource res = new ResResource(mConfig, mResSpec, value);
mConfig.addResource(res, true);
mResSpec.addResource(res, true);
}
public void replace(ResValue value) throws AndrolibException {
ResResource res = new ResResource(mConfig, mResSpec, value);
mConfig.addResource(res, true);
mResSpec.addResource(res, true);
}
@Override
public String toString() {
return getFilePath();
}
@Override
public String toString() {
return getFilePath();
}
}

View File

@ -26,125 +26,121 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResTable {
private final AndrolibResources mAndRes;
private final AndrolibResources mAndRes;
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>();
private final Set<ResPackage> mFramePackages =
new LinkedHashSet<ResPackage>();
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>();
private final Set<ResPackage> mFramePackages = new LinkedHashSet<ResPackage>();
private String mFrameTag;
private String mFrameTag;
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
private Map<String, String> mPackageInfo = new LinkedHashMap<String, String>();
private Map<String, String> mSdkInfo = new LinkedHashMap<String, String>();
private Map<String, String> mPackageInfo = new LinkedHashMap<String, String>();
public ResTable() {
mAndRes = null;
}
public ResTable() {
mAndRes = null;
}
public ResTable(AndrolibResources andRes) {
mAndRes = andRes;
}
public ResTable(AndrolibResources andRes) {
mAndRes = andRes;
}
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 ResResSpec getResSpec(int resID) throws AndrolibException {
return getResSpec(new ResID(resID));
}
public Set<ResPackage> listMainPackages() {
return mMainPackages;
}
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
return getPackage(resID.package_).getResSpec(resID);
}
public Set<ResPackage> listFramePackages() {
return mFramePackages;
}
public Set<ResPackage> listMainPackages() {
return mMainPackages;
}
public ResPackage getPackage(int id) throws AndrolibException {
ResPackage pkg = mPackagesById.get(id);
if (pkg != null) {
return pkg;
}
if (mAndRes != null) {
return mAndRes.loadFrameworkPkg(this, id, mFrameTag);
}
throw new UndefinedResObject(String.format("package: id=%d", id));
}
public Set<ResPackage> listFramePackages() {
return mFramePackages;
}
public ResPackage getPackage(String name) throws AndrolibException {
ResPackage pkg = mPackagesByName.get(name);
if (pkg == null) {
throw new UndefinedResObject("package: name=" + name);
}
return pkg;
}
public ResPackage getPackage(int id) throws AndrolibException {
ResPackage pkg = mPackagesById.get(id);
if (pkg != null) {
return pkg;
}
if (mAndRes != null) {
return mAndRes.loadFrameworkPkg(this, id, mFrameTag);
}
throw new UndefinedResObject(String.format("package: id=%d", id));
}
public boolean hasPackage(int id) {
return mPackagesById.containsKey(id);
}
public ResPackage getPackage(String name) throws AndrolibException {
ResPackage pkg = mPackagesByName.get(name);
if (pkg == null) {
throw new UndefinedResObject("package: name=" + name);
}
return pkg;
}
public boolean hasPackage(String name) {
return mPackagesByName.containsKey(name);
}
public boolean hasPackage(int id) {
return mPackagesById.containsKey(id);
}
public ResValue getValue(String package_, String type, String name)
throws AndrolibException {
return getPackage(package_).getType(type).getResSpec(name)
.getDefaultResource().getValue();
}
public boolean hasPackage(String name) {
return mPackagesByName.containsKey(name);
}
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);
}
public ResValue getValue(String package_, String type, String name)
throws AndrolibException {
return getPackage(package_).getType(type).getResSpec(name)
.getDefaultResource().getValue();
}
mPackagesById.put(id, pkg);
mPackagesByName.put(name, pkg);
if (main) {
mMainPackages.add(pkg);
} else {
mFramePackages.add(pkg);
}
}
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);
}
public void setFrameTag(String tag) {
mFrameTag = tag;
}
mPackagesById.put(id, pkg);
mPackagesByName.put(name, pkg);
if (main) {
mMainPackages.add(pkg);
} else {
mFramePackages.add(pkg);
}
}
public Map<String, String> getSdkInfo() {
return mSdkInfo;
}
public void setFrameTag(String tag) {
mFrameTag = tag;
}
public void addSdkInfo(String key, String value) {
mSdkInfo.put(key, value);
}
public Map<String, String> getSdkInfo() {
return mSdkInfo;
}
public void clearSdkInfo() {
mSdkInfo.clear();
}
public void addPackageInfo(String key, String value) {
mPackageInfo.put(key, value);
}
public Map<String, String> getPackageInfo() {
return mPackageInfo;
}
public boolean isPackageInfoValueSet(String key) {
return (mPackageInfo.containsKey(key));
}
public void addSdkInfo(String key, String value) {
mSdkInfo.put(key, value);
}
public void clearSdkInfo() {
mSdkInfo.clear();
}
public void addPackageInfo(String key, String value) {
mPackageInfo.put(key, value);
}
public Map<String, String> getPackageInfo() {
return mPackageInfo;
}
public boolean isPackageInfoValueSet(String key) {
return (mPackageInfo.containsKey(key));
}
}

View File

@ -24,47 +24,44 @@ import java.util.*;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public final class ResType {
private final String mName;
private final Map<String, ResResSpec> mResSpecs =
new LinkedHashMap<String, ResResSpec>();
private final String mName;
private final Map<String, ResResSpec> mResSpecs = new LinkedHashMap<String, ResResSpec>();
private final ResTable mResTable;
private final ResPackage mPackage;
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 ResType(String name, ResTable resTable, ResPackage package_) {
this.mName = name;
this.mResTable = resTable;
this.mPackage = package_;
}
public String getName() {
return mName;
}
public String getName() {
return mName;
}
public Set<ResResSpec> listResSpecs() {
return new LinkedHashSet<ResResSpec>(mResSpecs.values());
}
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 UndefinedResObject(String.format(
"resource spec: %s/%s", getName(), name));
}
return spec;
}
public ResResSpec getResSpec(String name) throws AndrolibException {
ResResSpec spec = mResSpecs.get(name);
if (spec == null) {
throw new UndefinedResObject(String.format("resource spec: %s/%s",
getName(), name));
}
return spec;
}
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()));
}
}
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;
}
@Override
public String toString() {
return mName;
}
}

View File

@ -23,68 +23,68 @@ import java.util.Set;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValuesFile {
private final ResPackage mPackage;
private final ResType mType;
private final ResConfig mConfig;
private final Set<ResResource> mResources =
new LinkedHashSet<ResResource>();
private final ResPackage mPackage;
private final ResType mType;
private final ResConfig mConfig;
private final Set<ResResource> mResources = new LinkedHashSet<ResResource>();
public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) {
this.mPackage = pkg;
this.mType = type;
this.mConfig = config;
}
public ResValuesFile(ResPackage pkg, ResType type, ResConfig config) {
this.mPackage = pkg;
this.mType = type;
this.mConfig = config;
}
public String getPath() {
return "values" + mConfig.getFlags().getQualifiers()
+ "/" + mType.getName()
+ (mType.getName().endsWith("s") ? "" : "s")
+ ".xml";
}
public String getPath() {
return "values" + mConfig.getFlags().getQualifiers() + "/"
+ mType.getName() + (mType.getName().endsWith("s") ? "" : "s")
+ ".xml";
}
public Set<ResResource> listResources() {
return mResources;
}
public Set<ResResource> listResources() {
return mResources;
}
public ResType getType() {
return mType;
}
public ResType getType() {
return mType;
}
public ResConfig getConfig() {
return mConfig;
}
public ResConfig getConfig() {
return mConfig;
}
public boolean isSynthesized(ResResource res) {
return mPackage.isSynthesized(res.getResSpec().getId());
}
public boolean isSynthesized(ResResource res) {
return mPackage.isSynthesized(res.getResSpec().getId());
}
public void addResource(ResResource res) {
mResources.add(res);
}
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 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 = 17;
hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0);
hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
return hash;
}
@Override
public int hashCode() {
int hash = 17;
hash = 31 * hash + (this.mType != null ? this.mType.hashCode() : 0);
hash = 31 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
return hash;
}
}

View File

@ -22,70 +22,70 @@ import brut.androlib.res.xml.ResValuesXmlSerializable;
import brut.util.Duo;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
import org.apache.commons.lang3.StringUtils;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResArrayValue extends ResBagValue implements ResValuesXmlSerializable {
private String mRawItems;
ResArrayValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items) {
super(parent);
public class ResArrayValue extends ResBagValue implements
ResValuesXmlSerializable {
private String mRawItems;
mItems = new ResScalarValue[items.length];
for (int i = 0; i < items.length; i++) {
mItems[i] = items[i].m2;
}
}
ResArrayValue(ResReferenceValue parent, Duo<Integer, ResScalarValue>[] items) {
super(parent);
public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) {
super(parent);
mItems = items;
}
mItems = new ResScalarValue[items.length];
for (int i = 0; i < items.length; i++) {
mItems[i] = items[i].m2;
}
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = getType();
type = (type == null ? "" : type + "-") + "array";
// reference array (04 10 2012, BurgerZ)
if ("reference-array".equals(type)) {
type = "string-array";
}
// reference array (04 10 2012, BurgerZ)
serializer.startTag(null, type);
serializer.attribute(null, "name", res.getResSpec().getName());
for (int i = 0; i < mItems.length; i++) {
serializer.startTag(null, "item");
serializer.text(mItems[i].encodeAsResXmlItemValue());
serializer.endTag(null, "item");
}
serializer.endTag(null, type);
}
public ResArrayValue(ResReferenceValue parent, ResScalarValue[] items) {
super(parent);
mItems = items;
}
public String getType() throws AndrolibException {
if (mItems.length == 0) {
return null;
}
String type = mItems[0].getType();
for (int i = 1; i < mItems.length; i++) {
if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) {
return "string";
} else if (mItems[i].encodeAsResXmlItemValue().startsWith("@drawable")) {
return null;
} else if (!"string".equals(type) && !"integer".equals(type)) {
return null;
} else if (!type.equals(mItems[i].getType())) {
return null;
}
}
return type;
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
String type = getType();
type = (type == null ? "" : type + "-") + "array";
// reference array (04 10 2012, BurgerZ)
if ("reference-array".equals(type)) {
type = "string-array";
}
// reference array (04 10 2012, BurgerZ)
serializer.startTag(null, type);
serializer.attribute(null, "name", res.getResSpec().getName());
for (int i = 0; i < mItems.length; i++) {
serializer.startTag(null, "item");
serializer.text(mItems[i].encodeAsResXmlItemValue());
serializer.endTag(null, "item");
}
serializer.endTag(null, type);
}
private final ResScalarValue[] mItems;
public String getType() throws AndrolibException {
if (mItems.length == 0) {
return null;
}
String type = mItems[0].getType();
for (int i = 1; i < mItems.length; i++) {
if (mItems[i].encodeAsResXmlItemValue().startsWith("@string")) {
return "string";
} else if (mItems[i].encodeAsResXmlItemValue().startsWith(
"@drawable")) {
return null;
} else if (!"string".equals(type) && !"integer".equals(type)) {
return null;
} else if (!type.equals(mItems[i].getType())) {
return null;
}
}
return type;
}
public static final int BAG_KEY_ARRAY_START = 0x02000000;
private final ResScalarValue[] mItems;
public static final int BAG_KEY_ARRAY_START = 0x02000000;
}

View File

@ -28,148 +28,148 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResAttr extends ResBagValue implements ResValuesXmlSerializable {
ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max,
Boolean l10n) {
super(parentVal);
mType = type;
mMin = min;
mMax = max;
mL10n = l10n;
}
ResAttr(ResReferenceValue parentVal, int type, Integer min, Integer max,
Boolean l10n) {
super(parentVal);
mType = type;
mMin = min;
mMax = max;
mL10n = l10n;
}
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
return null;
}
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
return null;
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = getTypeAsString();
@Override
public void serializeToResValuesXml(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);
}
if (mMin != null) {
serializer.attribute(null, "min", mMin.toString());
}
if (mMax != null) {
serializer.attribute(null, "max", mMax.toString());
}
if (mL10n != null && mL10n) {
serializer.attribute(null, "localization", "suggested");
}
serializeBody(serializer, res);
serializer.endTag(null, "attr");
}
serializer.startTag(null, "attr");
serializer.attribute(null, "name", res.getResSpec().getName());
if (type != null) {
serializer.attribute(null, "format", type);
}
if (mMin != null) {
serializer.attribute(null, "min", mMin.toString());
}
if (mMax != null) {
serializer.attribute(null, "max", mMax.toString());
}
if (mL10n != null && mL10n) {
serializer.attribute(null, "localization", "suggested");
}
serializeBody(serializer, res);
serializer.endTag(null, "attr");
}
public static ResAttr factory(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
ResPackage pkg) throws AndrolibException {
public static ResAttr factory(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory,
ResPackage pkg) throws AndrolibException {
int type = ((ResIntValue) items[0].m2).getValue();
int scalarType = type & 0xffff;
Integer min = null, max = null;
Boolean l10n = null;
int i;
for (i = 1; i < items.length; i++) {
switch (items[i].m1) {
case BAG_KEY_ATTR_MIN:
min = ((ResIntValue) items[i].m2).getValue();
continue;
case BAG_KEY_ATTR_MAX:
max = ((ResIntValue) items[i].m2).getValue();
continue;
case BAG_KEY_ATTR_L10N:
l10n = ((ResIntValue) items[i].m2).getValue() != 0;
continue;
}
break;
}
int type = ((ResIntValue) items[0].m2).getValue();
int scalarType = type & 0xffff;
Integer min = null, max = null;
Boolean l10n = null;
int i;
for (i = 1; i < items.length; i++) {
switch (items[i].m1) {
case BAG_KEY_ATTR_MIN:
min = ((ResIntValue) items[i].m2).getValue();
continue;
case BAG_KEY_ATTR_MAX:
max = ((ResIntValue) items[i].m2).getValue();
continue;
case BAG_KEY_ATTR_L10N:
l10n = ((ResIntValue) items[i].m2).getValue() != 0;
continue;
}
break;
}
if (i == items.length) {
return new ResAttr(parent, scalarType, min, max, l10n);
}
Duo<ResReferenceValue, ResIntValue>[] attrItems = new Duo[items.length
- i];
int j = 0;
for (; i < items.length; i++) {
int resId = items[i].m1;
pkg.addSynthesizedRes(resId);
attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>(
factory.newReference(resId, null),
(ResIntValue) items[i].m2);
}
switch (type & 0xff0000) {
case TYPE_ENUM:
return new ResEnumAttr(parent, scalarType, min, max, l10n,
attrItems);
case TYPE_FLAGS:
return new ResFlagsAttr(parent, scalarType, min, max, l10n,
attrItems);
}
if (i == items.length) {
return new ResAttr(parent, scalarType, min, max, l10n);
}
Duo<ResReferenceValue, ResIntValue>[] attrItems =
new Duo[items.length - i];
int j = 0;
for (; i < items.length; i++) {
int resId = items[i].m1;
pkg.addSynthesizedRes(resId);
attrItems[j++] = new Duo<ResReferenceValue, ResIntValue>(
factory.newReference(resId, null), (ResIntValue) items[i].m2);
}
switch (type & 0xff0000) {
case TYPE_ENUM:
return new ResEnumAttr(
parent, scalarType, min, max, l10n, attrItems);
case TYPE_FLAGS:
return new ResFlagsAttr(
parent, scalarType, min, max, l10n, attrItems);
}
throw new AndrolibException("Could not decode attr value");
}
throw new AndrolibException("Could not decode attr value");
}
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
}
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);
}
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 int mType;
private final Integer mMin;
private final Integer mMax;
private final Boolean mL10n;
private final int mType;
private final Integer mMin;
private final Integer mMax;
private final Boolean mL10n;
public static final int BAG_KEY_ATTR_TYPE = 0x01000000;
private static final int BAG_KEY_ATTR_MIN = 0x01000001;
private static final int BAG_KEY_ATTR_MAX = 0x01000002;
private static final int BAG_KEY_ATTR_L10N = 0x01000003;
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;
public static final int BAG_KEY_ATTR_TYPE = 0x01000000;
private static final int BAG_KEY_ATTR_MIN = 0x01000001;
private static final int BAG_KEY_ATTR_MAX = 0x01000002;
private static final int BAG_KEY_ATTR_L10N = 0x01000003;
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;
private static final int TYPE_ENUM = 0x00010000;
private static final int TYPE_FLAGS = 0x00020000;
private static final int TYPE_ENUM = 0x00010000;
private static final int TYPE_FLAGS = 0x00020000;
}

View File

@ -27,38 +27,39 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResBagValue extends ResValue implements ResValuesXmlSerializable {
protected final ResReferenceValue mParent;
protected final ResReferenceValue mParent;
public ResBagValue(ResReferenceValue parent) {
this.mParent = parent;
}
public ResBagValue(ResReferenceValue parent) {
this.mParent = parent;
}
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName();
if ("style".equals(type)) {
new ResStyleValue(mParent, new Duo[0], null)
.serializeToResValuesXml(serializer, res);
return;
}
if ("array".equals(type)) {
new ResArrayValue(mParent, new Duo[0])
.serializeToResValuesXml(serializer, res);
return;
}
if ("plurals".equals(type)) {
new ResPluralsValue(mParent, new Duo[0])
.serializeToResValuesXml(serializer, res);
return;
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName();
if ("style".equals(type)) {
new ResStyleValue(mParent, new Duo[0], null)
.serializeToResValuesXml(serializer, res);
return;
}
if ("array".equals(type)) {
new ResArrayValue(mParent, new Duo[0]).serializeToResValuesXml(
serializer, res);
return;
}
if ("plurals".equals(type)) {
new ResPluralsValue(mParent, new Duo[0]).serializeToResValuesXml(
serializer, res);
return;
}
serializer.startTag(null, "item");
serializer.attribute(null, "type", type);
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item");
}
serializer.startTag(null, "item");
serializer.attribute(null, "type", type);
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item");
}
public ResReferenceValue getParent() {
return mParent;
}
public ResReferenceValue getParent() {
return mParent;
}
}

View File

@ -20,18 +20,19 @@ package brut.androlib.res.data.value;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResBoolValue extends ResScalarValue {
private final boolean mValue;
private final boolean mValue;
public ResBoolValue(boolean value, String rawValue) {
super("bool", rawValue);
this.mValue = value;
}
public ResBoolValue(boolean value, String rawValue) {
super("bool", rawValue);
this.mValue = value;
}
public boolean getValue() {
return mValue;
}
public boolean getValue() {
return mValue;
}
protected String encodeAsResXml() {
return mValue ? "true" : "false";
}
@Override
protected String encodeAsResXml() {
return mValue ? "true" : "false";
}
}

View File

@ -20,12 +20,12 @@ package brut.androlib.res.data.value;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResColorValue extends ResIntValue {
public ResColorValue(int value, String rawValue) {
super(value, rawValue, "color");
}
public ResColorValue(int value, String rawValue) {
super(value, rawValue, "color");
}
@Override
protected String encodeAsResXml() {
return String.format("#%08x", mValue);
}
@Override
protected String encodeAsResXml() {
return String.format("#%08x", mValue);
}
}

View File

@ -23,12 +23,12 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResDimenValue extends ResIntValue {
public ResDimenValue(int value, String rawValue) {
super(value, rawValue, "dimen");
}
public ResDimenValue(int value, String rawValue) {
super(value, rawValue, "dimen");
}
@Override
protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue);
}
@Override
protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(TypedValue.TYPE_DIMENSION, mValue);
}
}

View File

@ -28,57 +28,55 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResEnumAttr extends ResAttr {
ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max,
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
super(parent, type, min, max, l10n);
mItems = items;
}
ResEnumAttr(ResReferenceValue parent, int type, Integer min, Integer max,
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
super(parent, type, min, max, l10n);
mItems = items;
}
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (value instanceof ResIntValue) {
String ret = decodeValue(((ResIntValue) value).getValue());
if (ret != null) {
return ret;
}
}
return super.convertToResXmlFormat(value);
}
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (value instanceof ResIntValue) {
String ret = decodeValue(((ResIntValue) value).getValue());
if (ret != null) {
return ret;
}
}
return super.convertToResXmlFormat(value);
}
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
int intVal = duo.m2.getValue();
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
int intVal = duo.m2.getValue();
serializer.startTag(null, "enum");
serializer.attribute(null, "name", duo.m1.getReferent().getName());
serializer.attribute(null, "value", String.valueOf(intVal));
serializer.endTag(null, "enum");
}
}
serializer.startTag(null, "enum");
serializer.attribute(null, "name", duo.m1.getReferent().getName());
serializer.attribute(null, "value", String.valueOf(intVal));
serializer.endTag(null, "enum");
}
}
private String decodeValue(int value) throws AndrolibException {
String value2 = mItemsCache.get(value);
if (value2 == null) {
ResReferenceValue ref = null;
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
if (duo.m2.getValue() == value) {
ref = duo.m1;
break;
}
}
if (ref != null) {
value2 = ref.getReferent().getName();
mItemsCache.put(value, value2);
}
}
return value2;
}
private String decodeValue(int value) throws AndrolibException {
String value2 = mItemsCache.get(value);
if (value2 == null) {
ResReferenceValue ref = null;
for (Duo<ResReferenceValue, ResIntValue> duo : mItems) {
if (duo.m2.getValue() == value) {
ref = duo.m1;
break;
}
}
if (ref != null) {
value2 = ref.getReferent().getName();
mItemsCache.put(value, value2);
}
}
return value2;
}
private final Duo<ResReferenceValue, ResIntValue>[] mItems;
private final Map<Integer, String> mItemsCache =
new HashMap<Integer, String>();
private final Duo<ResReferenceValue, ResIntValue>[] mItems;
private final Map<Integer, String> mItemsCache = new HashMap<Integer, String>();
}

View File

@ -22,21 +22,21 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFileValue extends ResValue {
private final String mPath;
private final String mPath;
public ResFileValue(String path) {
this.mPath = path;
}
public ResFileValue(String path) {
this.mPath = path;
}
public String getPath() {
return mPath;
}
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);
}
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

@ -28,133 +28,133 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFlagsAttr extends ResAttr {
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max, Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
super(parent, type, min, max, l10n);
ResFlagsAttr(ResReferenceValue parent, int type, Integer min, Integer max,
Boolean l10n, Duo<ResReferenceValue, ResIntValue>[] items) {
super(parent, type, min, max, l10n);
mItems = new FlagItem[items.length];
for (int i = 0; i < items.length; i++) {
mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue());
}
}
mItems = new FlagItem[items.length];
for (int i = 0; i < items.length; i++) {
mItems[i] = new FlagItem(items[i].m1, items[i].m2.getValue());
}
}
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (! (value instanceof ResIntValue)) {
return super.convertToResXmlFormat(value);
}
loadFlags();
int intVal = ((ResIntValue) value).getValue();
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (!(value instanceof ResIntValue)) {
return super.convertToResXmlFormat(value);
}
loadFlags();
int intVal = ((ResIntValue) value).getValue();
if (intVal == 0) {
return renderFlags(mZeroFlags);
}
if (intVal == 0) {
return renderFlags(mZeroFlags);
}
FlagItem[] flagItems = new FlagItem[mFlags.length];
int[] flags = new int[mFlags.length];
int flagsCount = 0;
for (int i = 0; i < mFlags.length; i++) {
FlagItem flagItem = mFlags[i];
int flag = flagItem.flag;
FlagItem[] flagItems = new FlagItem[mFlags.length];
int[] flags = new int[mFlags.length];
int flagsCount = 0;
for (int i = 0; i < mFlags.length; i++) {
FlagItem flagItem = mFlags[i];
int flag = flagItem.flag;
if ((intVal & flag) != flag) {
continue;
}
if ((intVal & flag) != flag) {
continue;
}
if (! isSubpartOf(flag, flags)) {
flags[flagsCount] = flag;
flagItems[flagsCount++] = flagItem;
}
}
return renderFlags(Arrays.copyOf(flagItems, flagsCount));
}
if (!isSubpartOf(flag, flags)) {
flags[flagsCount] = flag;
flagItems[flagsCount++] = flagItem;
}
}
return renderFlags(Arrays.copyOf(flagItems, flagsCount));
}
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (int i = 0; i < mItems.length; i++) {
FlagItem item = mItems[i];
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (int i = 0; i < mItems.length; i++) {
FlagItem item = mItems[i];
serializer.startTag(null, "flag");
serializer.attribute(null, "name", item.getValue());
serializer.attribute(null, "value",
String.format("0x%08x", item.flag));
serializer.endTag(null, "flag");
}
}
serializer.startTag(null, "flag");
serializer.attribute(null, "name", item.getValue());
serializer.attribute(null, "value",
String.format("0x%08x", item.flag));
serializer.endTag(null, "flag");
}
}
private boolean isSubpartOf(int flag, int[] flags) {
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & flag) == flag) {
return true;
}
}
return false;
}
private boolean isSubpartOf(int flag, int[] flags) {
for (int i = 0; i < flags.length; i++) {
if ((flags[i] & flag) == flag) {
return true;
}
}
return false;
}
private String renderFlags(FlagItem[] flags) throws AndrolibException {
String ret = "";
for (int i = 0; i < flags.length; i++) {
ret += "|" + flags[i].getValue();
}
if (ret.isEmpty()) {
return ret;
}
return ret.substring(1);
}
private String renderFlags(FlagItem[] flags) throws AndrolibException {
String ret = "";
for (int i = 0; i < flags.length; i++) {
ret += "|" + flags[i].getValue();
}
if (ret.isEmpty()) {
return ret;
}
return ret.substring(1);
}
private void loadFlags() {
if (mFlags != null) {
return;
}
private void loadFlags() {
if (mFlags != null) {
return;
}
FlagItem[] zeroFlags = new FlagItem[mItems.length];
int zeroFlagsCount = 0;
FlagItem[] flags = new FlagItem[mItems.length];
int flagsCount = 0;
FlagItem[] zeroFlags = new FlagItem[mItems.length];
int zeroFlagsCount = 0;
FlagItem[] flags = new FlagItem[mItems.length];
int flagsCount = 0;
for (int i = 0; i < mItems.length; i++) {
FlagItem item = mItems[i];
if (item.flag == 0) {
zeroFlags[zeroFlagsCount++] = item;
} else {
flags[flagsCount++] = item;
}
}
for (int i = 0; i < mItems.length; i++) {
FlagItem item = mItems[i];
if (item.flag == 0) {
zeroFlags[zeroFlagsCount++] = item;
} else {
flags[flagsCount++] = item;
}
}
mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount);
mFlags = Arrays.copyOf(flags, flagsCount);
mZeroFlags = Arrays.copyOf(zeroFlags, zeroFlagsCount);
mFlags = Arrays.copyOf(flags, flagsCount);
Arrays.sort(mFlags, new Comparator<FlagItem>() {
public int compare(FlagItem o1, FlagItem o2) {
return Integer.valueOf(Integer.bitCount(o2.flag))
.compareTo(Integer.bitCount(o1.flag));
}
});
}
Arrays.sort(mFlags, new Comparator<FlagItem>() {
@Override
public int compare(FlagItem o1, FlagItem o2) {
return Integer.valueOf(Integer.bitCount(o2.flag)).compareTo(
Integer.bitCount(o1.flag));
}
});
}
private final FlagItem[] mItems;
private final FlagItem[] mItems;
private FlagItem[] mZeroFlags;
private FlagItem[] mFlags;
private FlagItem[] mZeroFlags;
private FlagItem[] mFlags;
private static class FlagItem {
public final ResReferenceValue ref;
public final int flag;
public String value;
public FlagItem(ResReferenceValue ref, int flag) {
this.ref = ref;
this.flag = flag;
}
private static class FlagItem {
public final ResReferenceValue ref;
public final int flag;
public String value;
public FlagItem(ResReferenceValue ref, int flag) {
this.ref = ref;
this.flag = flag;
}
public String getValue() throws AndrolibException {
if (value == null) {
value = ref.getReferent().getName();
}
return value;
}
}
public String getValue() throws AndrolibException {
if (value == null) {
value = ref.getReferent().getName();
}
return value;
}
}
}

View File

@ -20,18 +20,19 @@ package brut.androlib.res.data.value;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFloatValue extends ResScalarValue {
private final float mValue;
private final float mValue;
public ResFloatValue(float value, String rawValue) {
super("float", rawValue);
this.mValue = value;
}
public ResFloatValue(float value, String rawValue) {
super("float", rawValue);
this.mValue = value;
}
public float getValue() {
return mValue;
}
public float getValue() {
return mValue;
}
protected String encodeAsResXml() {
return String.valueOf(mValue);
}
@Override
protected String encodeAsResXml() {
return String.valueOf(mValue);
}
}

View File

@ -23,12 +23,12 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFractionValue extends ResIntValue {
public ResFractionValue(int value, String rawValue) {
super(value, rawValue, "fraction");
}
public ResFractionValue(int value, String rawValue) {
super(value, rawValue, "fraction");
}
@Override
protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue);
}
@Override
protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(TypedValue.TYPE_FRACTION, mValue);
}
}

View File

@ -26,10 +26,13 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResIdValue extends ResValue implements ResValuesXmlSerializable {
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "item");
serializer.attribute(null, "type", res.getResSpec().getType().getName());
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item");
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "item");
serializer
.attribute(null, "type", res.getResSpec().getType().getName());
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item");
}
}

View File

@ -23,24 +23,25 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResIntValue extends ResScalarValue {
protected final int mValue;
private int type;
protected final int mValue;
private int type;
public ResIntValue(int value, String rawValue, int type) {
this(value, rawValue, "integer");
this.type = type;
}
public ResIntValue(int value, String rawValue, int type) {
this(value, rawValue, "integer");
this.type = type;
}
public ResIntValue(int value, String rawValue, String type) {
super(type, rawValue);
this.mValue = value;
}
public ResIntValue(int value, String rawValue, String type) {
super(type, rawValue);
this.mValue = value;
}
public int getValue() {
return mValue;
}
public int getValue() {
return mValue;
}
protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(type, mValue);
}
@Override
protected String encodeAsResXml() throws AndrolibException {
return TypedValue.coerceToString(type, mValue);
}
}

View File

@ -28,54 +28,57 @@ import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializable {
ResPluralsValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items) {
super(parent);
public class ResPluralsValue extends ResBagValue implements
ResValuesXmlSerializable {
ResPluralsValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items) {
super(parent);
mItems = new ResScalarValue[6];
for (int i = 0; i < items.length; i++) {
mItems[items[i].m1 - BAG_KEY_PLURALS_START] =
(ResScalarValue) items[i].m2;
}
}
mItems = new ResScalarValue[6];
for (int i = 0; i < items.length; i++) {
mItems[items[i].m1 - BAG_KEY_PLURALS_START] = items[i].m2;
}
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
serializer.startTag(null, "plurals");
serializer.attribute(null, "name", res.getResSpec().getName());
for (int i = 0; i < mItems.length; i++) {
ResScalarValue item = mItems[i];
if (item == null) {
continue;
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
serializer.startTag(null, "plurals");
serializer.attribute(null, "name", res.getResSpec().getName());
for (int i = 0; i < mItems.length; i++) {
ResScalarValue item = mItems[i];
if (item == null) {
continue;
}
ResScalarValue rawValue = item;
ResScalarValue rawValue = item;
serializer.startTag(null, "item");
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue.encodeAsResXmlValue())) {
serializer.text(item.encodeAsResXmlValueExt());
} else {
String recode = item.encodeAsResXmlValue();
//Dirty, but working fix @miuirussia
for (int j = 0; j < 10; j++) {
recode = StringUtils.replace(recode, "%" + Integer.toString(j) + "$" + Integer.toString(j) + "$", "%" + Integer.toString(j) + "$");
}
serializer.text(recode);
}
serializer.endTag(null, "item");
}
serializer.endTag(null, "plurals");
}
serializer.startTag(null, "item");
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue
.encodeAsResXmlValue())) {
serializer.text(item.encodeAsResXmlValueExt());
} else {
String recode = item.encodeAsResXmlValue();
// Dirty, but working fix @miuirussia
for (int j = 0; j < 10; j++) {
recode = StringUtils.replace(
recode,
"%" + Integer.toString(j) + "$"
+ Integer.toString(j) + "$",
"%" + Integer.toString(j) + "$");
}
serializer.text(recode);
}
serializer.endTag(null, "item");
}
serializer.endTag(null, "plurals");
}
private final ResScalarValue[] mItems;
private final ResScalarValue[] mItems;
public static final int BAG_KEY_PLURALS_START = 0x01000004;
public static final int BAG_KEY_PLURALS_END = 0x01000009;
private static final String[] QUANTITY_MAP =
new String[] {"other", "zero", "one", "two", "few", "many"};
public static final int BAG_KEY_PLURALS_START = 0x01000004;
public static final int BAG_KEY_PLURALS_END = 0x01000009;
private static final String[] QUANTITY_MAP = new String[] { "other",
"zero", "one", "two", "few", "many" };
}

View File

@ -24,44 +24,44 @@ 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;
private final ResPackage mPackage;
private final boolean mTheme;
public ResReferenceValue(ResPackage package_, int value, String rawValue) {
this(package_, value, rawValue, false);
}
public ResReferenceValue(ResPackage package_, int value, String rawValue,
boolean theme) {
super(value, rawValue, "reference");
mPackage = package_;
mTheme = theme;
}
public ResReferenceValue(ResPackage package_, int value, String rawValue) {
this(package_, value, rawValue, false);
}
protected String encodeAsResXml() throws AndrolibException {
if (isNull()) {
return "@null";
}
public ResReferenceValue(ResPackage package_, int value, String rawValue,
boolean theme) {
super(value, rawValue, "reference");
mPackage = package_;
mTheme = theme;
}
ResResSpec spec = getReferent();
boolean newId =
spec.hasDefaultResource() &&
spec.getDefaultResource().getValue() instanceof ResIdValue;
@Override
protected String encodeAsResXml() throws AndrolibException {
if (isNull()) {
return "@null";
}
// generate the beginning to fix @android
String mStart = (mTheme ? '?' : '@') + (newId ? "+" : "");
//mStart = mStart.replace("@android", "@*android");
return mStart +
spec.getFullName(mPackage,
mTheme && spec.getType().getName().equals("attr"));
}
ResResSpec spec = getReferent();
boolean newId = spec.hasDefaultResource()
&& spec.getDefaultResource().getValue() instanceof ResIdValue;
public ResResSpec getReferent() throws AndrolibException {
return mPackage.getResTable().getResSpec(getValue());
}
// generate the beginning to fix @android
String mStart = (mTheme ? '?' : '@') + (newId ? "+" : "");
// mStart = mStart.replace("@android", "@*android");
public boolean isNull() {
return mValue == 0;
}
return mStart
+ spec.getFullName(mPackage, mTheme
&& spec.getType().getName().equals("attr"));
}
public ResResSpec getReferent() throws AndrolibException {
return mPackage.getResTable().getResSpec(getValue());
}
public boolean isNull() {
return mValue == 0;
}
}

View File

@ -27,105 +27,107 @@ import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public abstract class ResScalarValue extends ResValue
implements ResXmlEncodable, ResValuesXmlSerializable {
protected final String mType;
protected final String mRawValue;
public abstract class ResScalarValue extends ResValue implements
ResXmlEncodable, ResValuesXmlSerializable {
protected final String mType;
protected final String mRawValue;
protected ResScalarValue(String type, String rawValue) {
mType = type;
mRawValue = rawValue;
}
protected ResScalarValue(String type, String rawValue) {
mType = type;
mRawValue = rawValue;
}
public String encodeAsResXmlAttr() throws AndrolibException {
if (mRawValue != null) {
return mRawValue;
}
return encodeAsResXml().replace("@android:", "@*android:");
}
@Override
public String encodeAsResXmlAttr() throws AndrolibException {
if (mRawValue != null) {
return mRawValue;
}
return encodeAsResXml().replace("@android:", "@*android:");
}
public String encodeAsResXmlItemValue() throws AndrolibException {
return encodeAsResXmlValue().replace("@android:", "@*android:");
}
public String encodeAsResXmlItemValue() throws AndrolibException {
return encodeAsResXmlValue().replace("@android:", "@*android:");
}
public String encodeAsResXmlValue() throws AndrolibException {
if (mRawValue != null) {
return mRawValue;
}
return encodeAsResXmlValueExt().replace("@android:", "@*android:");
}
@Override
public String encodeAsResXmlValue() throws AndrolibException {
if (mRawValue != null) {
return mRawValue;
}
return encodeAsResXmlValueExt().replace("@android:", "@*android:");
}
public String encodeAsResXmlValueExt() throws AndrolibException {
String rawValue = mRawValue;
if (rawValue != null) {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
int count = 1;
StringBuilder result = new StringBuilder();
String tmp1[] = rawValue.split("%%", -1);
int tmp1_sz = tmp1.length;
for(int i=0;i<tmp1_sz;i++) {
String cur1 = tmp1[i];
String tmp2[] = cur1.split("%", -1);
int tmp2_sz = tmp2.length;
for(int j=0;j<tmp2_sz;j++) {
String cur2 = tmp2[j];
result.append(cur2);
if(j != (tmp2_sz-1)) {
result.append('%').append(count).append('$');
count++;
}
}
if(i != (tmp1_sz-1)) {
result.append("%%");
}
}
rawValue = result.toString();
}
return rawValue;
}
return encodeAsResXml();
}
public String encodeAsResXmlValueExt() throws AndrolibException {
String rawValue = mRawValue;
if (rawValue != null) {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
int count = 1;
StringBuilder result = new StringBuilder();
String tmp1[] = rawValue.split("%%", -1);
int tmp1_sz = tmp1.length;
for (int i = 0; i < tmp1_sz; i++) {
String cur1 = tmp1[i];
String tmp2[] = cur1.split("%", -1);
int tmp2_sz = tmp2.length;
for (int j = 0; j < tmp2_sz; j++) {
String cur2 = tmp2[j];
result.append(cur2);
if (j != (tmp2_sz - 1)) {
result.append('%').append(count).append('$');
count++;
}
}
if (i != (tmp1_sz - 1)) {
result.append("%%");
}
}
rawValue = result.toString();
}
return rawValue;
}
return encodeAsResXml();
}
@Override
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName();
boolean item = !"reference".equals(mType) && !type.equals(mType);
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = res.getResSpec().getType().getName();
boolean item = !"reference".equals(mType) && !type.equals(mType);
String body = encodeAsResXmlValue();
// check for resource reference
if (body.contains("@")){
if(!res.getFilePath().contains("string")) {
item = true;
}
}
// check for using attrib as node or item
String tagName = item ? "item" : type;
serializer.startTag(null, tagName);
if (item) {
serializer.attribute(null, "type", type);
}
serializer.attribute(null, "name", res.getResSpec().getName());
String body = encodeAsResXmlValue();
serializeExtraXmlAttrs(serializer, res);
// check for resource reference
if (body.contains("@")) {
if (!res.getFilePath().contains("string")) {
item = true;
}
}
if (! body.isEmpty()) {
serializer.ignorableWhitespace(body);
}
// check for using attrib as node or item
String tagName = item ? "item" : type;
serializer.endTag(null, tagName);
}
serializer.startTag(null, tagName);
if (item) {
serializer.attribute(null, "type", type);
}
serializer.attribute(null, "name", res.getResSpec().getName());
public String getType() {
return mType;
}
serializeExtraXmlAttrs(serializer, res);
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
ResResource res) throws IOException {
}
if (!body.isEmpty()) {
serializer.ignorableWhitespace(body);
}
protected abstract String encodeAsResXml() throws AndrolibException;
serializer.endTag(null, tagName);
}
public String getType() {
return mType;
}
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
ResResource res) throws IOException {
}
protected abstract String encodeAsResXml() throws AndrolibException;
}

View File

@ -27,40 +27,41 @@ import org.xmlpull.v1.XmlSerializer;
*/
public class ResStringValue extends ResScalarValue {
public ResStringValue(String value) {
this(value, "string");
}
public ResStringValue(String value) {
this(value, "string");
}
public ResStringValue(String value, String type) {
super(type, value);
}
public ResStringValue(String value, String type) {
super(type, value);
}
@Override
public String encodeAsResXmlAttr() {
return ResXmlEncoders.encodeAsResXmlAttr(mRawValue);
}
@Override
public String encodeAsResXmlAttr() {
return ResXmlEncoders.encodeAsResXmlAttr(mRawValue);
}
@Override
public String encodeAsResXmlItemValue() {
return ResXmlEncoders.enumerateNonPositionalSubstitutions(
ResXmlEncoders.encodeAsXmlValue(mRawValue));
}
@Override
public String encodeAsResXmlItemValue() {
return ResXmlEncoders
.enumerateNonPositionalSubstitutions(ResXmlEncoders
.encodeAsXmlValue(mRawValue));
}
@Override
public String encodeAsResXmlValue() {
return ResXmlEncoders.encodeAsXmlValue(mRawValue);
}
@Override
public String encodeAsResXmlValue() {
return ResXmlEncoders.encodeAsXmlValue(mRawValue);
}
@Override
protected String encodeAsResXml() throws AndrolibException {
throw new UnsupportedOperationException();
}
@Override
protected String encodeAsResXml() throws AndrolibException {
throw new UnsupportedOperationException();
}
@Override
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
ResResource res) throws IOException {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) {
serializer.attribute(null, "formatted", "false");
}
}
@Override
protected void serializeExtraXmlAttrs(XmlSerializer serializer,
ResResource res) throws IOException {
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(mRawValue)) {
serializer.attribute(null, "formatted", "false");
}
}
}

View File

@ -27,53 +27,54 @@ import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResStyleValue extends ResBagValue implements ResValuesXmlSerializable {
ResStyleValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
super(parent);
public class ResStyleValue extends ResBagValue implements
ResValuesXmlSerializable {
ResStyleValue(ResReferenceValue parent,
Duo<Integer, ResScalarValue>[] items, ResValueFactory factory) {
super(parent);
mItems = new Duo[items.length];
for (int i = 0; i < items.length; i++) {
mItems[i] = new Duo<ResReferenceValue, ResScalarValue>(
factory.newReference(items[i].m1, null), items[i].m2);
}
}
mItems = new Duo[items.length];
for (int i = 0; i < items.length; i++) {
mItems[i] = new Duo<ResReferenceValue, ResScalarValue>(
factory.newReference(items[i].m1, null), items[i].m2);
}
}
@Override
public void serializeToResValuesXml(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.encodeAsResXmlAttr());
}
for (int i = 0; i < mItems.length; i++) {
ResResSpec spec = mItems[i].m1.getReferent();
// hacky-fix remove bad ReferenceVars
if (spec.getDefaultResource().getValue().toString().contains("ResReferenceValue@")) {
continue;
}
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
String value = attr.convertToResXmlFormat(mItems[i].m2);
@Override
public void serializeToResValuesXml(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.encodeAsResXmlAttr());
}
for (int i = 0; i < mItems.length; i++) {
ResResSpec spec = mItems[i].m1.getReferent();
if (value == null) {
value = mItems[i].m2.encodeAsResXmlValue();
}
// hacky-fix remove bad ReferenceVars
if (spec.getDefaultResource().getValue().toString()
.contains("ResReferenceValue@")) {
continue;
}
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
String value = attr.convertToResXmlFormat(mItems[i].m2);
if (value == null) {
continue;
}
if (value == null) {
value = mItems[i].m2.encodeAsResXmlValue();
}
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");
}
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");
}
private final Duo<ResReferenceValue, ResScalarValue>[] mItems;
private final Duo<ResReferenceValue, ResScalarValue>[] mItems;
}

View File

@ -25,77 +25,77 @@ import brut.util.Duo;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValueFactory {
private final ResPackage mPackage;
private final ResPackage mPackage;
public ResValueFactory(ResPackage pakage_) {
this.mPackage = pakage_;
}
public ResValueFactory(ResPackage pakage_) {
this.mPackage = pakage_;
}
public ResScalarValue factory(int type, int value, String rawValue)
throws AndrolibException {
switch (type) {
case TypedValue.TYPE_REFERENCE:
return newReference(value, rawValue);
case TypedValue.TYPE_ATTRIBUTE:
return newReference(value, rawValue, true);
case TypedValue.TYPE_STRING:
return new ResStringValue(rawValue);
case TypedValue.TYPE_FLOAT:
return new ResFloatValue(Float.intBitsToFloat(value), rawValue);
case TypedValue.TYPE_DIMENSION:
return new ResDimenValue(value, rawValue);
case TypedValue.TYPE_FRACTION:
return new ResFractionValue(value, rawValue);
case TypedValue.TYPE_INT_BOOLEAN:
return new ResBoolValue(value != 0, rawValue);
}
public ResScalarValue factory(int type, int value, String rawValue)
throws AndrolibException {
switch (type) {
case TypedValue.TYPE_REFERENCE:
return newReference(value, rawValue);
case TypedValue.TYPE_ATTRIBUTE:
return newReference(value, rawValue, true);
case TypedValue.TYPE_STRING:
return new ResStringValue(rawValue);
case TypedValue.TYPE_FLOAT:
return new ResFloatValue(Float.intBitsToFloat(value), rawValue);
case TypedValue.TYPE_DIMENSION:
return new ResDimenValue(value, rawValue);
case TypedValue.TYPE_FRACTION:
return new ResFractionValue(value, rawValue);
case TypedValue.TYPE_INT_BOOLEAN:
return new ResBoolValue(value != 0, rawValue);
}
if (type >= TypedValue.TYPE_FIRST_COLOR_INT
&& type <= TypedValue.TYPE_LAST_COLOR_INT) {
return new ResColorValue(value, rawValue);
}
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
return new ResIntValue(value, rawValue, type);
}
if (type >= TypedValue.TYPE_FIRST_COLOR_INT
&& type <= TypedValue.TYPE_LAST_COLOR_INT) {
return new ResColorValue(value, rawValue);
}
if (type >= TypedValue.TYPE_FIRST_INT
&& type <= TypedValue.TYPE_LAST_INT) {
return new ResIntValue(value, rawValue, type);
}
throw new AndrolibException("Invalid value type: "+ type);
}
throw new AndrolibException("Invalid value type: " + type);
}
public ResValue factory(String value) {
if (value.startsWith("res/")) {
return new ResFileValue(value);
}
return new ResStringValue(value);
}
public ResValue factory(String value) {
if (value.startsWith("res/")) {
return new ResFileValue(value);
}
return new ResStringValue(value);
}
public ResBagValue bagFactory(int parent,
Duo<Integer, ResScalarValue>[] items) throws AndrolibException {
ResReferenceValue parentVal = newReference(parent, null);
public ResBagValue bagFactory(int parent,
Duo<Integer, ResScalarValue>[] items) throws AndrolibException {
ResReferenceValue parentVal = newReference(parent, null);
if (items.length == 0) {
return new ResBagValue(parentVal);
}
int key = items[0].m1;
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
return ResAttr.factory(parentVal, items, this, mPackage);
}
if (key == ResArrayValue.BAG_KEY_ARRAY_START) {
return new ResArrayValue(parentVal, items);
}
if (key >= ResPluralsValue.BAG_KEY_PLURALS_START
&& key <= ResPluralsValue.BAG_KEY_PLURALS_END) {
return new ResPluralsValue(parentVal, items);
}
return new ResStyleValue(parentVal, items, this);
}
if (items.length == 0) {
return new ResBagValue(parentVal);
}
int key = items[0].m1;
if (key == ResAttr.BAG_KEY_ATTR_TYPE) {
return ResAttr.factory(parentVal, items, this, mPackage);
}
if (key == ResArrayValue.BAG_KEY_ARRAY_START) {
return new ResArrayValue(parentVal, items);
}
if (key >= ResPluralsValue.BAG_KEY_PLURALS_START
&& key <= ResPluralsValue.BAG_KEY_PLURALS_END) {
return new ResPluralsValue(parentVal, items);
}
return new ResStyleValue(parentVal, items, this);
}
public ResReferenceValue newReference(int resID, String rawValue) {
return newReference(resID, rawValue, false);
}
public ResReferenceValue newReference(int resID, String rawValue) {
return newReference(resID, rawValue, false);
}
public ResReferenceValue newReference(int resID, String rawValue,
boolean theme) {
return new ResReferenceValue(mPackage, resID, rawValue, theme);
}
public ResReferenceValue newReference(int resID, String rawValue,
boolean theme) {
return new ResReferenceValue(mPackage, resID, rawValue, theme);
}
}

View File

@ -34,412 +34,406 @@ import org.apache.commons.io.input.CountingInputStream;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ARSCDecoder {
public static ARSCData decode(InputStream arscStream,
boolean findFlagsOffsets, boolean keepBroken)
throws AndrolibException {
return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable());
}
public static ARSCData decode(InputStream arscStream,
boolean findFlagsOffsets, boolean keepBroken, ResTable resTable)
throws AndrolibException {
try {
ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable,
findFlagsOffsets, keepBroken);
ResPackage[] pkgs = decoder.readTable();
return new ARSCData(
pkgs,
decoder.mFlagsOffsets == null ? null :
decoder.mFlagsOffsets.toArray(new FlagsOffset[0]),
resTable);
} catch (IOException ex) {
throw new AndrolibException("Could not decode arsc file", ex);
}
}
private ARSCDecoder(InputStream arscStream, ResTable resTable,
boolean storeFlagsOffsets, boolean keepBroken) {
if (storeFlagsOffsets) {
arscStream = mCountIn = new CountingInputStream(arscStream);
mFlagsOffsets = new ArrayList<FlagsOffset>();
} else {
mCountIn = null;
mFlagsOffsets = null;
}
mIn = new ExtDataInput(new LEDataInputStream(arscStream));
mResTable = resTable;
mKeepBroken = keepBroken;
}
private ResPackage[] readTable() throws IOException, AndrolibException {
nextChunkCheckType(Header.TYPE_TABLE);
int packageCount = mIn.readInt();
mTableStrings = StringBlock.read(mIn);
ResPackage[] packages = new ResPackage[packageCount];
nextChunk();
for (int i = 0; i < packageCount; i++) {
packages[i] = readPackage();
}
// store package
if (this.mResTable.isPackageInfoValueSet("cur_package") != true) {
this.mResTable.addPackageInfo("cur_package", packages[0].getName());
}
return packages;
}
private ResPackage readPackage() throws IOException, AndrolibException {
checkChunkType(Header.TYPE_PACKAGE);
int id = (byte) mIn.readInt();
String name = mIn.readNulEndedString(128, true);
/*typeNameStrings*/ mIn.skipInt();
/*typeNameCount*/ mIn.skipInt();
/*specNameStrings*/ mIn.skipInt();
/*specNameCount*/ mIn.skipInt();
mTypeNames = StringBlock.read(mIn);
mSpecNames = StringBlock.read(mIn);
mResId = id << 24;
mPkg = new ResPackage(mResTable, id, name);
nextChunk();
while (mHeader.type == Header.TYPE_TYPE) {
readType();
}
return mPkg;
}
private ResType readType() throws AndrolibException, IOException {
checkChunkType(Header.TYPE_TYPE);
byte id = mIn.readByte();
mIn.skipBytes(3);
int entryCount = mIn.readInt();
mMissingResSpecs = new boolean[entryCount];
Arrays.fill(mMissingResSpecs, true);
if (mFlagsOffsets != null) {
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
}
/*flags*/ mIn.skipBytes(entryCount * 4);
mResId = (0xff000000 & mResId) | id << 16;
mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg);
mPkg.addType(mType);
while (nextChunk().type == Header.TYPE_CONFIG) {
readConfig();
}
addMissingResSpecs();
return mType;
}
private ResConfig readConfig() throws IOException, AndrolibException {
checkChunkType(Header.TYPE_CONFIG);
/*typeId*/ mIn.skipInt();
int entryCount = mIn.readInt();
/*entriesStart*/ mIn.skipInt();
ResConfigFlags flags = readConfigFlags();
int[] entryOffsets = mIn.readIntArray(entryCount);
if (flags.isInvalid) {
String resName = mType.getName() + flags.getQualifiers();
if (mKeepBroken) {
LOGGER.warning(
"Invalid config flags detected: " + resName);
} else {
LOGGER.warning(
"Invalid config flags detected. Dropping resources: " + resName);
}
}
mConfig = flags.isInvalid && ! mKeepBroken ?
null : mPkg.getOrCreateConfig(flags);
for (int i = 0; i < entryOffsets.length; i++) {
if (entryOffsets[i] != -1) {
mMissingResSpecs[i] = false;
mResId = (mResId & 0xffff0000) | i;
readEntry();
}
}
return mConfig;
}
private void readEntry() throws IOException, AndrolibException {
/*size*/ mIn.skipBytes(2);
short flags = mIn.readShort();
int specNamesId = mIn.readInt();
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ?
readValue() : readComplexEntry();
if (mConfig == null) {
return;
}
ResID resId = new ResID(mResId);
ResResSpec spec;
if (mPkg.hasResSpec(resId)) {
spec = mPkg.getResSpec(resId);
} else {
spec = new ResResSpec(
resId, mSpecNames.getString(specNamesId), mPkg, mType);
mPkg.addResSpec(spec);
mType.addResSpec(spec);
}
ResResource res = new ResResource(mConfig, spec, value);
mConfig.addResource(res);
spec.addResource(res);
mPkg.addResource(res);
}
private ResBagValue readComplexEntry() throws IOException,
AndrolibException {
int parent = mIn.readInt();
int count = mIn.readInt();
ResValueFactory factory = mPkg.getValueFactory();
Duo<Integer, ResScalarValue>[] items = new Duo[count];
for (int i = 0; i < count; i++) {
items[i] = new Duo<Integer, ResScalarValue>(
mIn.readInt(), (ResScalarValue) readValue());
}
return factory.bagFactory(parent, items);
}
private ResValue readValue() throws IOException, AndrolibException {
/*size*/ mIn.skipCheckShort((short) 8);
/*zero*/ mIn.skipCheckByte((byte) 0);
byte type = mIn.readByte();
int data = mIn.readInt();
return type == TypedValue.TYPE_STRING ?
mPkg.getValueFactory().factory(mTableStrings.getHTML(data)) :
mPkg.getValueFactory().factory(type, data, null);
}
private ResConfigFlags readConfigFlags() throws IOException, AndrolibException {
int size = mIn.readInt();
if (size < 28) {
throw new AndrolibException("Config size < 28");
}
boolean isInvalid = false;
short mcc = mIn.readShort();
short mnc = mIn.readShort();
char[] language = new char[]{
(char) mIn.readByte(), (char) mIn.readByte()};
char[] country = new char[]{
(char) mIn.readByte(), (char) mIn.readByte()};
byte orientation = mIn.readByte();
byte touchscreen = mIn.readByte();
short density = mIn.readShort();
byte keyboard = mIn.readByte();
byte navigation = mIn.readByte();
byte inputFlags = mIn.readByte();
/*inputPad0*/ mIn.skipBytes(1);
short screenWidth = mIn.readShort();
short screenHeight = mIn.readShort();
short sdkVersion = mIn.readShort();
/*minorVersion, now must always be 0*/ mIn.skipBytes(2);
byte screenLayout = 0;
byte uiMode = 0;
short smallestScreenWidthDp = 0;
if (size >= 32) {
screenLayout = mIn.readByte();
uiMode = mIn.readByte();
smallestScreenWidthDp = mIn.readShort();
}
short screenWidthDp = 0;
short screenHeightDp = 0;
if (size >= 36) {
screenWidthDp = mIn.readShort();
screenHeightDp = mIn.readShort();
}
short layoutDirection = 0;
if (size >= 38 && sdkVersion >= 17 && !this.mPkg.getName().equalsIgnoreCase("com.htc")) {
layoutDirection = mIn.readShort();
}
int exceedingSize = size - KNOWN_CONFIG_BYTES;
if (exceedingSize > 0) {
byte[] buf = new byte[exceedingSize];
mIn.readFully(buf);
BigInteger exceedingBI = new BigInteger(1, buf);
if (exceedingBI.equals(BigInteger.ZERO)) {
LOGGER.fine(String.format(
"Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
KNOWN_CONFIG_BYTES));
} else {
LOGGER.warning(String.format(
"Config flags size > %d. Exceeding bytes: 0x%X.",
KNOWN_CONFIG_BYTES, exceedingBI));
isInvalid = true;
}
}
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection, orientation,
touchscreen, density, keyboard, navigation, inputFlags,
screenWidth, screenHeight, sdkVersion, screenLayout, uiMode,
smallestScreenWidthDp, screenWidthDp, screenHeightDp, isInvalid);
}
private void addMissingResSpecs() throws AndrolibException {
int resId = mResId & 0xffff0000;
for (int i = 0; i < mMissingResSpecs.length; i++) {
if (! mMissingResSpecs[i]) {
continue;
}
ResResSpec spec = new ResResSpec(new ResID(resId | i),
String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType);
mPkg.addResSpec(spec);
mType.addResSpec(spec);
ResValue value = new ResBoolValue(false, null);
ResResource res = new ResResource(
mPkg.getOrCreateConfig(new ResConfigFlags()), spec, value);
mPkg.addResource(res);
mConfig.addResource(res);
spec.addResource(res);
}
}
private Header nextChunk() throws IOException {
return mHeader = Header.read(mIn);
}
private void checkChunkType(int expectedType) throws AndrolibException {
if (mHeader.type != expectedType) {
throw new AndrolibException(String.format(
"Invalid chunk type: expected=0x%08x, got=0x%08x",
expectedType, mHeader.type));
}
}
private void nextChunkCheckType(int expectedType)
throws IOException, AndrolibException {
nextChunk();
checkChunkType(expectedType);
}
private final ExtDataInput mIn;
private final ResTable mResTable;
private final CountingInputStream mCountIn;
private final List<FlagsOffset> mFlagsOffsets;
private final boolean mKeepBroken;
private Header mHeader;
private StringBlock mTableStrings;
private StringBlock mTypeNames;
private StringBlock mSpecNames;
private ResPackage mPkg;
private ResType mType;
private ResConfig mConfig;
private int mResId;
private boolean[] mMissingResSpecs;
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
public static class Header {
public final short type;
public final int chunkSize;
public Header(short type, int size) {
this.type = type;
this.chunkSize = size;
}
public static Header read(ExtDataInput in) throws IOException {
short type;
try {
type = in.readShort();
} catch (EOFException ex) {
return new Header(TYPE_NONE, 0);
}
in.skipBytes(2);
return new Header(type, in.readInt());
}
public final static short
TYPE_NONE = -1,
TYPE_TABLE = 0x0002,
TYPE_PACKAGE = 0x0200,
TYPE_TYPE = 0x0202,
TYPE_CONFIG = 0x0201;
}
public static class FlagsOffset {
public final int offset;
public final int count;
public FlagsOffset(int offset, int count) {
this.offset = offset;
this.count = count;
}
}
private static final Logger LOGGER =
Logger.getLogger(ARSCDecoder.class.getName());
private static final int KNOWN_CONFIG_BYTES = 36;
public static class ARSCData {
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets,
ResTable resTable) {
mPackages = packages;
mFlagsOffsets = flagsOffsets;
mResTable = resTable;
}
public FlagsOffset[] getFlagsOffsets() {
return mFlagsOffsets;
}
public ResPackage[] getPackages() {
return mPackages;
}
public ResPackage getOnePackage() throws AndrolibException {
if (mPackages.length != 1) {
throw new AndrolibException(
"Arsc file contains zero or multiple packages");
}
return mPackages[0];
}
public ResTable getResTable() {
return mResTable;
}
private final ResPackage[] mPackages;
private final FlagsOffset[] mFlagsOffsets;
private final ResTable mResTable;
}
public static ARSCData decode(InputStream arscStream,
boolean findFlagsOffsets, boolean keepBroken)
throws AndrolibException {
return decode(arscStream, findFlagsOffsets, keepBroken, new ResTable());
}
public static ARSCData decode(InputStream arscStream,
boolean findFlagsOffsets, boolean keepBroken, ResTable resTable)
throws AndrolibException {
try {
ARSCDecoder decoder = new ARSCDecoder(arscStream, resTable,
findFlagsOffsets, keepBroken);
ResPackage[] pkgs = decoder.readTable();
return new ARSCData(pkgs, decoder.mFlagsOffsets == null ? null
: decoder.mFlagsOffsets.toArray(new FlagsOffset[0]),
resTable);
} catch (IOException ex) {
throw new AndrolibException("Could not decode arsc file", ex);
}
}
private ARSCDecoder(InputStream arscStream, ResTable resTable,
boolean storeFlagsOffsets, boolean keepBroken) {
if (storeFlagsOffsets) {
arscStream = mCountIn = new CountingInputStream(arscStream);
mFlagsOffsets = new ArrayList<FlagsOffset>();
} else {
mCountIn = null;
mFlagsOffsets = null;
}
mIn = new ExtDataInput(new LEDataInputStream(arscStream));
mResTable = resTable;
mKeepBroken = keepBroken;
}
private ResPackage[] readTable() throws IOException, AndrolibException {
nextChunkCheckType(Header.TYPE_TABLE);
int packageCount = mIn.readInt();
mTableStrings = StringBlock.read(mIn);
ResPackage[] packages = new ResPackage[packageCount];
nextChunk();
for (int i = 0; i < packageCount; i++) {
packages[i] = readPackage();
}
// store package
if (this.mResTable.isPackageInfoValueSet("cur_package") != true) {
this.mResTable.addPackageInfo("cur_package", packages[0].getName());
}
return packages;
}
private ResPackage readPackage() throws IOException, AndrolibException {
checkChunkType(Header.TYPE_PACKAGE);
int id = (byte) mIn.readInt();
String name = mIn.readNulEndedString(128, true);
/* typeNameStrings */mIn.skipInt();
/* typeNameCount */mIn.skipInt();
/* specNameStrings */mIn.skipInt();
/* specNameCount */mIn.skipInt();
mTypeNames = StringBlock.read(mIn);
mSpecNames = StringBlock.read(mIn);
mResId = id << 24;
mPkg = new ResPackage(mResTable, id, name);
nextChunk();
while (mHeader.type == Header.TYPE_TYPE) {
readType();
}
return mPkg;
}
private ResType readType() throws AndrolibException, IOException {
checkChunkType(Header.TYPE_TYPE);
byte id = mIn.readByte();
mIn.skipBytes(3);
int entryCount = mIn.readInt();
mMissingResSpecs = new boolean[entryCount];
Arrays.fill(mMissingResSpecs, true);
if (mFlagsOffsets != null) {
mFlagsOffsets.add(new FlagsOffset(mCountIn.getCount(), entryCount));
}
/* flags */mIn.skipBytes(entryCount * 4);
mResId = (0xff000000 & mResId) | id << 16;
mType = new ResType(mTypeNames.getString(id - 1), mResTable, mPkg);
mPkg.addType(mType);
while (nextChunk().type == Header.TYPE_CONFIG) {
readConfig();
}
addMissingResSpecs();
return mType;
}
private ResConfig readConfig() throws IOException, AndrolibException {
checkChunkType(Header.TYPE_CONFIG);
/* typeId */mIn.skipInt();
int entryCount = mIn.readInt();
/* entriesStart */mIn.skipInt();
ResConfigFlags flags = readConfigFlags();
int[] entryOffsets = mIn.readIntArray(entryCount);
if (flags.isInvalid) {
String resName = mType.getName() + flags.getQualifiers();
if (mKeepBroken) {
LOGGER.warning("Invalid config flags detected: " + resName);
} else {
LOGGER.warning("Invalid config flags detected. Dropping resources: "
+ resName);
}
}
mConfig = flags.isInvalid && !mKeepBroken ? null : mPkg
.getOrCreateConfig(flags);
for (int i = 0; i < entryOffsets.length; i++) {
if (entryOffsets[i] != -1) {
mMissingResSpecs[i] = false;
mResId = (mResId & 0xffff0000) | i;
readEntry();
}
}
return mConfig;
}
private void readEntry() throws IOException, AndrolibException {
/* size */mIn.skipBytes(2);
short flags = mIn.readShort();
int specNamesId = mIn.readInt();
ResValue value = (flags & ENTRY_FLAG_COMPLEX) == 0 ? readValue()
: readComplexEntry();
if (mConfig == null) {
return;
}
ResID resId = new ResID(mResId);
ResResSpec spec;
if (mPkg.hasResSpec(resId)) {
spec = mPkg.getResSpec(resId);
} else {
spec = new ResResSpec(resId, mSpecNames.getString(specNamesId),
mPkg, mType);
mPkg.addResSpec(spec);
mType.addResSpec(spec);
}
ResResource res = new ResResource(mConfig, spec, value);
mConfig.addResource(res);
spec.addResource(res);
mPkg.addResource(res);
}
private ResBagValue readComplexEntry() throws IOException,
AndrolibException {
int parent = mIn.readInt();
int count = mIn.readInt();
ResValueFactory factory = mPkg.getValueFactory();
Duo<Integer, ResScalarValue>[] items = new Duo[count];
for (int i = 0; i < count; i++) {
items[i] = new Duo<Integer, ResScalarValue>(mIn.readInt(),
(ResScalarValue) readValue());
}
return factory.bagFactory(parent, items);
}
private ResValue readValue() throws IOException, AndrolibException {
/* size */mIn.skipCheckShort((short) 8);
/* zero */mIn.skipCheckByte((byte) 0);
byte type = mIn.readByte();
int data = mIn.readInt();
return type == TypedValue.TYPE_STRING ? mPkg.getValueFactory().factory(
mTableStrings.getHTML(data)) : mPkg.getValueFactory().factory(
type, data, null);
}
private ResConfigFlags readConfigFlags() throws IOException,
AndrolibException {
int size = mIn.readInt();
if (size < 28) {
throw new AndrolibException("Config size < 28");
}
boolean isInvalid = false;
short mcc = mIn.readShort();
short mnc = mIn.readShort();
char[] language = new char[] { (char) mIn.readByte(),
(char) mIn.readByte() };
char[] country = new char[] { (char) mIn.readByte(),
(char) mIn.readByte() };
byte orientation = mIn.readByte();
byte touchscreen = mIn.readByte();
short density = mIn.readShort();
byte keyboard = mIn.readByte();
byte navigation = mIn.readByte();
byte inputFlags = mIn.readByte();
/* inputPad0 */mIn.skipBytes(1);
short screenWidth = mIn.readShort();
short screenHeight = mIn.readShort();
short sdkVersion = mIn.readShort();
/* minorVersion, now must always be 0 */mIn.skipBytes(2);
byte screenLayout = 0;
byte uiMode = 0;
short smallestScreenWidthDp = 0;
if (size >= 32) {
screenLayout = mIn.readByte();
uiMode = mIn.readByte();
smallestScreenWidthDp = mIn.readShort();
}
short screenWidthDp = 0;
short screenHeightDp = 0;
if (size >= 36) {
screenWidthDp = mIn.readShort();
screenHeightDp = mIn.readShort();
}
short layoutDirection = 0;
if (size >= 38 && sdkVersion >= 17
&& !this.mPkg.getName().equalsIgnoreCase("com.htc")) {
layoutDirection = mIn.readShort();
}
int exceedingSize = size - KNOWN_CONFIG_BYTES;
if (exceedingSize > 0) {
byte[] buf = new byte[exceedingSize];
mIn.readFully(buf);
BigInteger exceedingBI = new BigInteger(1, buf);
if (exceedingBI.equals(BigInteger.ZERO)) {
LOGGER.fine(String
.format("Config flags size > %d, but exceeding bytes are all zero, so it should be ok.",
KNOWN_CONFIG_BYTES));
} else {
LOGGER.warning(String.format(
"Config flags size > %d. Exceeding bytes: 0x%X.",
KNOWN_CONFIG_BYTES, exceedingBI));
isInvalid = true;
}
}
return new ResConfigFlags(mcc, mnc, language, country, layoutDirection,
orientation, touchscreen, density, keyboard, navigation,
inputFlags, screenWidth, screenHeight, sdkVersion,
screenLayout, uiMode, smallestScreenWidthDp, screenWidthDp,
screenHeightDp, isInvalid);
}
private void addMissingResSpecs() throws AndrolibException {
int resId = mResId & 0xffff0000;
for (int i = 0; i < mMissingResSpecs.length; i++) {
if (!mMissingResSpecs[i]) {
continue;
}
ResResSpec spec = new ResResSpec(new ResID(resId | i),
String.format("APKTOOL_DUMMY_%04x", i), mPkg, mType);
mPkg.addResSpec(spec);
mType.addResSpec(spec);
ResValue value = new ResBoolValue(false, null);
ResResource res = new ResResource(
mPkg.getOrCreateConfig(new ResConfigFlags()), spec, value);
mPkg.addResource(res);
mConfig.addResource(res);
spec.addResource(res);
}
}
private Header nextChunk() throws IOException {
return mHeader = Header.read(mIn);
}
private void checkChunkType(int expectedType) throws AndrolibException {
if (mHeader.type != expectedType) {
throw new AndrolibException(String.format(
"Invalid chunk type: expected=0x%08x, got=0x%08x",
expectedType, mHeader.type));
}
}
private void nextChunkCheckType(int expectedType) throws IOException,
AndrolibException {
nextChunk();
checkChunkType(expectedType);
}
private final ExtDataInput mIn;
private final ResTable mResTable;
private final CountingInputStream mCountIn;
private final List<FlagsOffset> mFlagsOffsets;
private final boolean mKeepBroken;
private Header mHeader;
private StringBlock mTableStrings;
private StringBlock mTypeNames;
private StringBlock mSpecNames;
private ResPackage mPkg;
private ResType mType;
private ResConfig mConfig;
private int mResId;
private boolean[] mMissingResSpecs;
private final static short ENTRY_FLAG_COMPLEX = 0x0001;
public static class Header {
public final short type;
public final int chunkSize;
public Header(short type, int size) {
this.type = type;
this.chunkSize = size;
}
public static Header read(ExtDataInput in) throws IOException {
short type;
try {
type = in.readShort();
} catch (EOFException ex) {
return new Header(TYPE_NONE, 0);
}
in.skipBytes(2);
return new Header(type, in.readInt());
}
public final static short TYPE_NONE = -1, TYPE_TABLE = 0x0002,
TYPE_PACKAGE = 0x0200, TYPE_TYPE = 0x0202,
TYPE_CONFIG = 0x0201;
}
public static class FlagsOffset {
public final int offset;
public final int count;
public FlagsOffset(int offset, int count) {
this.offset = offset;
this.count = count;
}
}
private static final Logger LOGGER = Logger.getLogger(ARSCDecoder.class
.getName());
private static final int KNOWN_CONFIG_BYTES = 36;
public static class ARSCData {
public ARSCData(ResPackage[] packages, FlagsOffset[] flagsOffsets,
ResTable resTable) {
mPackages = packages;
mFlagsOffsets = flagsOffsets;
mResTable = resTable;
}
public FlagsOffset[] getFlagsOffsets() {
return mFlagsOffsets;
}
public ResPackage[] getPackages() {
return mPackages;
}
public ResPackage getOnePackage() throws AndrolibException {
if (mPackages.length != 1) {
throw new AndrolibException(
"Arsc file contains zero or multiple packages");
}
return mPackages[0];
}
public ResTable getResTable() {
return mResTable;
}
private final ResPackage[] mPackages;
private final FlagsOffset[] mFlagsOffsets;
private final ResTable mResTable;
}
}

View File

@ -28,112 +28,112 @@ import org.apache.commons.io.IOUtils;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class Res9patchStreamDecoder implements ResStreamDecoder {
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
byte[] data = IOUtils.toByteArray(in);
@Override
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
byte[] data = IOUtils.toByteArray(in);
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
int w = im.getWidth(), h = im.getHeight();
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
int w = im.getWidth(), h = im.getHeight();
BufferedImage im2 = new BufferedImage(
w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR);
if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) {
im2.getRaster().setRect(1, 1, im.getRaster());
} else {
im2.getGraphics().drawImage(im, 1, 1, null);
}
BufferedImage im2 = new BufferedImage(w + 2, h + 2,
BufferedImage.TYPE_4BYTE_ABGR);
if (im.getType() == BufferedImage.TYPE_4BYTE_ABGR) {
im2.getRaster().setRect(1, 1, im.getRaster());
} else {
im2.getGraphics().drawImage(im, 1, 1, null);
}
NinePatch np = getNinePatch(data);
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
NinePatch np = getNinePatch(data);
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
int[] xDivs = np.xDivs;
for (int i = 0; i < xDivs.length; i += 2) {
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
}
int[] xDivs = np.xDivs;
for (int i = 0; i < xDivs.length; i += 2) {
drawHLine(im2, 0, xDivs[i] + 1, xDivs[i + 1]);
}
int[] yDivs = np.yDivs;
for (int i = 0; i < yDivs.length; i += 2) {
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
}
int[] yDivs = np.yDivs;
for (int i = 0; i < yDivs.length; i += 2) {
drawVLine(im2, 0, yDivs[i] + 1, yDivs[i + 1]);
}
ImageIO.write(im2, "png", out);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
ImageIO.write(im2, "png", out);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
private NinePatch getNinePatch(byte[] data)
throws AndrolibException, IOException {
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
find9patchChunk(di);
return NinePatch.decode(di);
}
private NinePatch getNinePatch(byte[] data) throws AndrolibException,
IOException {
ExtDataInput di = new ExtDataInput(new ByteArrayInputStream(data));
find9patchChunk(di);
return NinePatch.decode(di);
}
private void find9patchChunk(DataInput di)
throws AndrolibException, IOException {
di.skipBytes(8);
while (true) {
int size;
try {
size = di.readInt();
} catch (IOException ex) {
throw new CantFind9PatchChunk("Cant find nine patch chunk", ex);
}
if (di.readInt() == NP_CHUNK_TYPE) {
return;
}
di.skipBytes(size + 4);
}
}
private void find9patchChunk(DataInput di) throws AndrolibException,
IOException {
di.skipBytes(8);
while (true) {
int size;
try {
size = di.readInt();
} catch (IOException ex) {
throw new CantFind9PatchChunk("Cant find nine patch chunk", ex);
}
if (di.readInt() == NP_CHUNK_TYPE) {
return;
}
di.skipBytes(size + 4);
}
}
private void drawHLine(BufferedImage im, int y, int x1, int x2) {
for (int x = x1; x <= x2; x++) {
im.setRGB(x, y, NP_COLOR);
}
}
private void drawHLine(BufferedImage im, int y, int x1, int x2) {
for (int x = x1; x <= x2; x++) {
im.setRGB(x, y, NP_COLOR);
}
}
private void drawVLine(BufferedImage im, int x, int y1, int y2) {
for (int y = y1; y <= y2; y++) {
im.setRGB(x, y, NP_COLOR);
}
}
private void drawVLine(BufferedImage im, int x, int y1, int y2) {
for (int y = y1; y <= y2; y++) {
im.setRGB(x, y, NP_COLOR);
}
}
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
private static final int NP_COLOR = 0xff000000;
private static final int NP_CHUNK_TYPE = 0x6e705463; // npTc
private static final int NP_COLOR = 0xff000000;
private static class NinePatch {
public final int padLeft, padRight, padTop, padBottom;
public final int[] xDivs, yDivs;
private static class NinePatch {
public final int padLeft, padRight, padTop, padBottom;
public final int[] xDivs, yDivs;
public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
int[] xDivs, int[] yDivs) {
this.padLeft = padLeft;
this.padRight = padRight;
this.padTop = padTop;
this.padBottom = padBottom;
this.xDivs = xDivs;
this.yDivs = yDivs;
}
public NinePatch(int padLeft, int padRight, int padTop, int padBottom,
int[] xDivs, int[] yDivs) {
this.padLeft = padLeft;
this.padRight = padRight;
this.padTop = padTop;
this.padBottom = padBottom;
this.xDivs = xDivs;
this.yDivs = yDivs;
}
public static NinePatch decode(ExtDataInput di) throws IOException {
di.skipBytes(1);
byte numXDivs = di.readByte();
byte numYDivs = di.readByte();
di.skipBytes(1);
di.skipBytes(8);
int padLeft = di.readInt();
int padRight = di.readInt();
int padTop = di.readInt();
int padBottom = di.readInt();
di.skipBytes(4);
int[] xDivs = di.readIntArray(numXDivs);
int[] yDivs = di.readIntArray(numYDivs);
public static NinePatch decode(ExtDataInput di) throws IOException {
di.skipBytes(1);
byte numXDivs = di.readByte();
byte numYDivs = di.readByte();
di.skipBytes(1);
di.skipBytes(8);
int padLeft = di.readInt();
int padRight = di.readInt();
int padTop = di.readInt();
int padBottom = di.readInt();
di.skipBytes(4);
int[] xDivs = di.readIntArray(numXDivs);
int[] yDivs = di.readIntArray(numYDivs);
return new NinePatch(padLeft, padRight, padTop, padBottom,
xDivs, yDivs);
}
}
return new NinePatch(padLeft, padRight, padTop, padBottom, xDivs,
yDivs);
}
}
}

View File

@ -25,31 +25,31 @@ import brut.androlib.res.data.value.ResScalarValue;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResAttrDecoder {
public String decode(int type, int value, String rawValue, int attrResId)
throws AndrolibException {
ResScalarValue resValue = mCurrentPackage.getValueFactory()
.factory(type, value, rawValue);
public String decode(int type, int value, String rawValue, int attrResId)
throws AndrolibException {
ResScalarValue resValue = mCurrentPackage.getValueFactory().factory(
type, value, rawValue);
String decoded = null;
if (attrResId != 0) {
ResAttr attr = (ResAttr) getCurrentPackage().getResTable()
.getResSpec(attrResId).getDefaultResource().getValue();
decoded = attr.convertToResXmlFormat(resValue);
}
String decoded = null;
if (attrResId != 0) {
ResAttr attr = (ResAttr) getCurrentPackage().getResTable()
.getResSpec(attrResId).getDefaultResource().getValue();
decoded = attr.convertToResXmlFormat(resValue);
}
return decoded != null ? decoded : resValue.encodeAsResXmlAttr();
}
return decoded != null ? decoded : resValue.encodeAsResXmlAttr();
}
public ResPackage getCurrentPackage() throws AndrolibException {
if (mCurrentPackage == null) {
throw new AndrolibException("Current package not set");
}
return mCurrentPackage;
}
public ResPackage getCurrentPackage() throws AndrolibException {
if (mCurrentPackage == null) {
throw new AndrolibException("Current package not set");
}
return mCurrentPackage;
}
public void setCurrentPackage(ResPackage currentPackage) {
mCurrentPackage = currentPackage;
}
public void setCurrentPackage(ResPackage currentPackage) {
mCurrentPackage = currentPackage;
}
private ResPackage mCurrentPackage;
private ResPackage mCurrentPackage;
}

View File

@ -31,120 +31,121 @@ import java.util.logging.Logger;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFileDecoder {
private final ResStreamDecoderContainer mDecoders;
private final ResStreamDecoderContainer mDecoders;
public ResFileDecoder(ResStreamDecoderContainer decoders) {
this.mDecoders = decoders;
}
public ResFileDecoder(ResStreamDecoderContainer decoders) {
this.mDecoders = decoders;
}
public void decode(ResResource res, Directory inDir, Directory outDir)
throws AndrolibException {
public void decode(ResResource res, Directory inDir, Directory outDir)
throws AndrolibException {
ResFileValue fileValue = (ResFileValue) res.getValue();
String inFileName = fileValue.getStrippedPath();
String outResName = res.getFilePath();
String typeName = res.getResSpec().getType().getName();
ResFileValue fileValue = (ResFileValue) res.getValue();
String inFileName = fileValue.getStrippedPath();
String outResName = res.getFilePath();
String typeName = res.getResSpec().getType().getName();
String ext = null;
String outFileName;
int extPos = inFileName.lastIndexOf(".");
if (extPos == -1) {
outFileName = outResName;
} else {
ext = inFileName.substring(extPos);
outFileName = outResName + ext;
}
String ext = null;
String outFileName;
int extPos = inFileName.lastIndexOf(".");
if (extPos == -1) {
outFileName = outResName;
} else {
ext = inFileName.substring(extPos);
outFileName = outResName + ext;
}
try {
if (typeName.equals("raw")) {
decode(inDir, inFileName, outDir, outFileName, "raw");
return;
}
if (typeName.equals("drawable") || typeName.equals("mipmap")) {
if (inFileName.toLowerCase().endsWith(".9.png")) {
outFileName = outResName + ".9" + ext;
// check for htc .r.9.png
if (inFileName.toLowerCase().endsWith(".r.9.png")) {
outFileName = outResName + ".r.9" + ext;
}
try {
if (typeName.equals("raw")) {
decode(inDir, inFileName, outDir, outFileName, "raw");
return;
}
if (typeName.equals("drawable") || typeName.equals("mipmap")) {
if (inFileName.toLowerCase().endsWith(".9.png")) {
outFileName = outResName + ".9" + ext;
try {
decode(
inDir, inFileName, outDir, outFileName, "9patch");
return;
} catch (CantFind9PatchChunk ex) {
LOGGER.log(Level.WARNING, String.format(
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
inFileName
), ex);
outDir.removeFile(outFileName);
outFileName = outResName + ext;
}
}
if (! ".xml".equals(ext)) {
decode(inDir, inFileName, outDir, outFileName, "raw");
return;
}
}
// check for htc .r.9.png
if (inFileName.toLowerCase().endsWith(".r.9.png")) {
outFileName = outResName + ".r.9" + ext;
}
decode(inDir, inFileName, outDir, outFileName, "xml");
} catch (AndrolibException ex) {
LOGGER.log(Level.SEVERE, String.format(
"Could not decode file, replacing by FALSE value: %s",
inFileName, outFileName), ex);
res.replace(new ResBoolValue(false, null));
}
}
try {
decode(inDir, inFileName, outDir, outFileName, "9patch");
return;
} catch (CantFind9PatchChunk ex) {
LOGGER.log(
Level.WARNING,
String.format(
"Cant find 9patch chunk in file: \"%s\". Renaming it to *.png.",
inFileName), ex);
outDir.removeFile(outFileName);
outFileName = outResName + ext;
}
}
if (!".xml".equals(ext)) {
decode(inDir, inFileName, outDir, outFileName, "raw");
return;
}
}
public void decode(Directory inDir, String inFileName, Directory outDir,
String outFileName, String decoder) throws AndrolibException {
InputStream in = null;
OutputStream out = null;
try {
in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName);
mDecoders.decode(in, out, decoder);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
} finally {
try{
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
}
decode(inDir, inFileName, outDir, outFileName, "xml");
} catch (AndrolibException ex) {
LOGGER.log(Level.SEVERE, String.format(
"Could not decode file, replacing by FALSE value: %s",
inFileName, outFileName), ex);
res.replace(new ResBoolValue(false, null));
}
}
public void decodeManifest(Directory inDir, String inFileName, Directory outDir,
String outFileName) throws AndrolibException {
InputStream in = null;
OutputStream out = null;
try {
in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName);
((XmlPullStreamDecoder)mDecoders.getDecoder("xml")).decodeManifest(in, out);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
} finally {
try{
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
}
public void decode(Directory inDir, String inFileName, Directory outDir,
String outFileName, String decoder) throws AndrolibException {
InputStream in = null;
OutputStream out = null;
try {
in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName);
mDecoders.decode(in, out, decoder);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
}
private final static Logger LOGGER =
Logger.getLogger(ResFileDecoder.class.getName());
public void decodeManifest(Directory inDir, String inFileName,
Directory outDir, String outFileName) throws AndrolibException {
InputStream in = null;
OutputStream out = null;
try {
in = inDir.getFileInput(inFileName);
out = outDir.getFileOutput(outFileName);
((XmlPullStreamDecoder) mDecoders.getDecoder("xml"))
.decodeManifest(in, out);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
} finally {
try {
if (in != null) {
in.close();
}
if (out != null) {
out.close();
}
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
}
private final static Logger LOGGER = Logger.getLogger(ResFileDecoder.class
.getName());
}

View File

@ -26,12 +26,13 @@ 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);
}
}
@Override
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

@ -24,6 +24,6 @@ import java.io.OutputStream;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResStreamDecoder {
public void decode(InputStream in, OutputStream out)
throws AndrolibException;
public void decode(InputStream in, OutputStream out)
throws AndrolibException;
}

View File

@ -26,23 +26,22 @@ 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>();
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 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 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);
}
public void setDecoder(String name, ResStreamDecoder decoder) {
mDecoders.put(name, decoder);
}
}

View File

@ -28,320 +28,313 @@ import java.util.logging.Logger;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
* @author Dmitry Skiba
*
* Block of strings, used in binary xml and arsc.
* Block of strings, used in binary xml and arsc.
*
* TODO: - implement get()
*
* TODO:
* - implement get()
*
*/
public class StringBlock {
/**
* Reads whole (including chunk type) string block from stream.
* Stream must be at the chunk type.
*/
public static StringBlock read(ExtDataInput reader) throws IOException {
reader.skipCheckInt(CHUNK_TYPE);
int chunkSize = reader.readInt();
int stringCount = reader.readInt();
int styleOffsetCount = reader.readInt();
int flags = reader.readInt();
int stringsOffset = reader.readInt();
int stylesOffset = reader.readInt();
/**
* Reads whole (including chunk type) string block from stream. Stream must
* be at the chunk type.
*/
public static StringBlock read(ExtDataInput reader) throws IOException {
reader.skipCheckInt(CHUNK_TYPE);
int chunkSize = reader.readInt();
int stringCount = reader.readInt();
int styleOffsetCount = reader.readInt();
int flags = reader.readInt();
int stringsOffset = reader.readInt();
int stylesOffset = reader.readInt();
StringBlock block = new StringBlock();
block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
block.m_stringOffsets = reader.readIntArray(stringCount);
block.m_stringOwns = new int[stringCount];
for (int i=0;i<stringCount;i++) {
block.m_stringOwns[i] = -1;
}
if (styleOffsetCount != 0) {
block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
}
{
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset) - stringsOffset;
if ((size % 4) != 0) {
throw new IOException("String data size is not multiple of 4 (" + size + ").");
}
block.m_strings = new byte[size];
reader.readFully(block.m_strings);
}
if (stylesOffset != 0) {
int size = (chunkSize - stylesOffset);
if ((size % 4) != 0) {
throw new IOException("Style data size is not multiple of 4 (" + size + ").");
}
block.m_styles = reader.readIntArray(size / 4);
}
StringBlock block = new StringBlock();
block.m_isUTF8 = (flags & UTF8_FLAG) != 0;
block.m_stringOffsets = reader.readIntArray(stringCount);
block.m_stringOwns = new int[stringCount];
for (int i = 0; i < stringCount; i++) {
block.m_stringOwns[i] = -1;
}
if (styleOffsetCount != 0) {
block.m_styleOffsets = reader.readIntArray(styleOffsetCount);
}
{
int size = ((stylesOffset == 0) ? chunkSize : stylesOffset)
- stringsOffset;
if ((size % 4) != 0) {
throw new IOException("String data size is not multiple of 4 ("
+ size + ").");
}
block.m_strings = new byte[size];
reader.readFully(block.m_strings);
}
if (stylesOffset != 0) {
int size = (chunkSize - stylesOffset);
if ((size % 4) != 0) {
throw new IOException("Style data size is not multiple of 4 ("
+ size + ").");
}
block.m_styles = reader.readIntArray(size / 4);
}
return block;
}
return block;
}
/**
* Returns number of strings in block.
*/
public int getCount() {
return m_stringOffsets != null
? m_stringOffsets.length
: 0;
}
/**
* Returns number of strings in block.
*/
public int getCount() {
return m_stringOffsets != null ? m_stringOffsets.length : 0;
}
/**
* Returns raw string (without any styling information) at specified index.
*/
public String getString(int index) {
if (index < 0
|| m_stringOffsets == null
|| index >= m_stringOffsets.length) {
return null;
}
int offset = m_stringOffsets[index];
int length;
/**
* Returns raw string (without any styling information) at specified index.
*/
public String getString(int index) {
if (index < 0 || m_stringOffsets == null
|| index >= m_stringOffsets.length) {
return null;
}
int offset = m_stringOffsets[index];
int length;
if (! m_isUTF8) {
length = getShort(m_strings, offset) * 2;
offset += 2;
} else {
offset += getVarint(m_strings, offset)[1];
int[] varint = getVarint(m_strings, offset);
offset += varint[1];
length = varint[0];
}
return decodeString(offset, length);
}
if (!m_isUTF8) {
length = getShort(m_strings, offset) * 2;
offset += 2;
} else {
offset += getVarint(m_strings, offset)[1];
int[] varint = getVarint(m_strings, offset);
offset += varint[1];
length = varint[0];
}
return decodeString(offset, length);
}
/**
* Not yet implemented.
*
* Returns string with style information (if any).
*/
public CharSequence get(int index) {
return getString(index);
}
/**
* Not yet implemented.
*
* Returns string with style information (if any).
*/
public CharSequence get(int index) {
return getString(index);
}
/**
* Returns string with style tags (html-like).
*/
public String getHTML(int index) {
String raw = getString(index);
if (raw == null) {
return raw;
}
int[] style = getStyle(index);
if (style == null) {
return ResXmlEncoders.escapeXmlChars(raw);
}
StringBuilder html = new StringBuilder(raw.length() + 32);
int[] opened = new int[style.length / 3];
int offset = 0, depth = 0;
while (true) {
int i = -1, j;
for (j = 0; j != style.length; j += 3) {
if (style[j + 1] == -1) {
continue;
}
if (i == -1 || style[i + 1] > style[j + 1]) {
i = j;
}
}
int start = ((i != -1) ? style[i + 1] : raw.length());
for (j = depth - 1; j >= 0; j--) {
int last = opened[j];
int end = style[last + 2];
if (end >= start) {
break;
}
if (offset <= end) {
html.append(ResXmlEncoders.escapeXmlChars(
raw.substring(offset, end + 1)));
offset = end + 1;
}
outputStyleTag(getString(style[last]), html, true);
}
depth = j + 1;
if (offset < start) {
html.append(ResXmlEncoders.escapeXmlChars(
raw.substring(offset, start)));
offset = start;
}
if (i == -1) {
break;
}
outputStyleTag(getString(style[i]), html, false);
style[i + 1] = -1;
opened[depth++] = i;
}
return html.toString();
}
/**
* Returns string with style tags (html-like).
*/
public String getHTML(int index) {
String raw = getString(index);
if (raw == null) {
return raw;
}
int[] style = getStyle(index);
if (style == null) {
return ResXmlEncoders.escapeXmlChars(raw);
}
StringBuilder html = new StringBuilder(raw.length() + 32);
int[] opened = new int[style.length / 3];
int offset = 0, depth = 0;
while (true) {
int i = -1, j;
for (j = 0; j != style.length; j += 3) {
if (style[j + 1] == -1) {
continue;
}
if (i == -1 || style[i + 1] > style[j + 1]) {
i = j;
}
}
int start = ((i != -1) ? style[i + 1] : raw.length());
for (j = depth - 1; j >= 0; j--) {
int last = opened[j];
int end = style[last + 2];
if (end >= start) {
break;
}
if (offset <= end) {
html.append(ResXmlEncoders.escapeXmlChars(raw.substring(
offset, end + 1)));
offset = end + 1;
}
outputStyleTag(getString(style[last]), html, true);
}
depth = j + 1;
if (offset < start) {
html.append(ResXmlEncoders.escapeXmlChars(raw.substring(offset,
start)));
offset = start;
}
if (i == -1) {
break;
}
outputStyleTag(getString(style[i]), html, false);
style[i + 1] = -1;
opened[depth++] = i;
}
return html.toString();
}
private void outputStyleTag(String tag, StringBuilder builder,
boolean close) {
builder.append('<');
if (close) {
builder.append('/');
}
private void outputStyleTag(String tag, StringBuilder builder, boolean close) {
builder.append('<');
if (close) {
builder.append('/');
}
int pos = tag.indexOf(';');
if (pos == -1) {
builder.append(tag);
} else {
builder.append(tag.substring(0, pos));
if (! close) {
boolean loop = true;
while (loop) {
int pos2 = tag.indexOf('=', pos + 1);
builder.append(' ').append(tag.substring(pos + 1, pos2))
.append("=\"");
pos = tag.indexOf(';', pos2 + 1);
int pos = tag.indexOf(';');
if (pos == -1) {
builder.append(tag);
} else {
builder.append(tag.substring(0, pos));
if (!close) {
boolean loop = true;
while (loop) {
int pos2 = tag.indexOf('=', pos + 1);
builder.append(' ').append(tag.substring(pos + 1, pos2))
.append("=\"");
pos = tag.indexOf(';', pos2 + 1);
String val;
if (pos != -1) {
val = tag.substring(pos2 + 1, pos);
} else {
loop = false;
val = tag.substring(pos2 + 1);
}
String val;
if (pos != -1) {
val = tag.substring(pos2 + 1, pos);
} else {
loop = false;
val = tag.substring(pos2 + 1);
}
builder.append(ResXmlEncoders.escapeXmlChars(val))
.append('"');
}
}
}
builder.append('>');
}
builder.append(ResXmlEncoders.escapeXmlChars(val)).append(
'"');
}
}
}
builder.append('>');
}
/**
* Finds index of the string.
* Returns -1 if the string was not found.
*/
public int find(String string) {
if (string == null) {
return -1;
}
for (int i = 0; i != m_stringOffsets.length; ++i) {
int offset = m_stringOffsets[i];
int length = getShort(m_strings, offset);
if (length != string.length()) {
continue;
}
int j = 0;
for (; j != length; ++j) {
offset += 2;
if (string.charAt(j) != getShort(m_strings, offset)) {
break;
}
}
if (j == length) {
return i;
}
}
return -1;
}
/**
* Finds index of the string. Returns -1 if the string was not found.
*/
public int find(String string) {
if (string == null) {
return -1;
}
for (int i = 0; i != m_stringOffsets.length; ++i) {
int offset = m_stringOffsets[i];
int length = getShort(m_strings, offset);
if (length != string.length()) {
continue;
}
int j = 0;
for (; j != length; ++j) {
offset += 2;
if (string.charAt(j) != getShort(m_strings, offset)) {
break;
}
}
if (j == length) {
return i;
}
}
return -1;
}
///////////////////////////////////////////// implementation
private StringBlock() {
}
// /////////////////////////////////////////// implementation
private StringBlock() {
}
/**
* Returns style information - array of int triplets,
* where in each triplet:
* * first int is index of tag name ('b','i', etc.)
* * second int is tag start index in string
* * third int is tag end index in string
*/
private int[] getStyle(int index) {
if (m_styleOffsets == null || m_styles == null
|| index >= m_styleOffsets.length) {
return null;
}
int offset = m_styleOffsets[index] / 4;
int style[];
{
int count = 0;
for (int i = offset; i < m_styles.length; ++i) {
if (m_styles[i] == -1) {
break;
}
count += 1;
}
if (count == 0 || (count % 3) != 0) {
return null;
}
style = new int[count];
}
for (int i = offset, j = 0; i < m_styles.length;) {
if (m_styles[i] == -1) {
break;
}
style[j++] = m_styles[i++];
}
return style;
}
/**
* Returns style information - array of int triplets, where in each triplet:
* * first int is index of tag name ('b','i', etc.) * second int is tag
* start index in string * third int is tag end index in string
*/
private int[] getStyle(int index) {
if (m_styleOffsets == null || m_styles == null
|| index >= m_styleOffsets.length) {
return null;
}
int offset = m_styleOffsets[index] / 4;
int style[];
{
int count = 0;
for (int i = offset; i < m_styles.length; ++i) {
if (m_styles[i] == -1) {
break;
}
count += 1;
}
if (count == 0 || (count % 3) != 0) {
return null;
}
style = new int[count];
}
for (int i = offset, j = 0; i < m_styles.length;) {
if (m_styles[i] == -1) {
break;
}
style[j++] = m_styles[i++];
}
return style;
}
private String decodeString(int offset, int length) {
try {
return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(
ByteBuffer.wrap(m_strings, offset, length)).toString();
} catch (CharacterCodingException ex) {
LOGGER.log(Level.WARNING, null, ex);
return null;
}
}
private String decodeString(int offset, int length) {
try {
return (m_isUTF8 ? UTF8_DECODER : UTF16LE_DECODER).decode(
ByteBuffer.wrap(m_strings, offset, length)).toString();
} catch (CharacterCodingException ex) {
LOGGER.log(Level.WARNING, null, ex);
return null;
}
}
private static final int getShort(byte[] array, int offset) {
return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
}
private static final int getShort(byte[] array, int offset) {
return (array[offset + 1] & 0xff) << 8 | array[offset] & 0xff;
}
private static final int getShort(int[] array, int offset) {
int value = array[offset / 4];
if ((offset % 4) / 2 == 0) {
return (value & 0xFFFF);
} else {
return (value >>> 16);
}
}
private static final int getShort(int[] array, int offset) {
int value = array[offset / 4];
if ((offset % 4) / 2 == 0) {
return (value & 0xFFFF);
} else {
return (value >>> 16);
}
}
private static final int[] getVarint(byte[] array, int offset) {
int val = array[offset];
boolean more = (val & 0x80) != 0;
val &= 0x7f;
private static final int[] getVarint(byte[] array, int offset) {
int val = array[offset];
boolean more = (val & 0x80) != 0;
val &= 0x7f;
if (! more) {
return new int[]{val, 1};
} else {
return new int[]{val << 8 | array[offset + 1] & 0xff, 2};
}
}
if (!more) {
return new int[] { val, 1 };
} else {
return new int[] { val << 8 | array[offset + 1] & 0xff, 2 };
}
}
public boolean touch(int index, int own) {
if (index < 0
|| m_stringOwns == null
|| index >= m_stringOwns.length) {
return false;
}
if(m_stringOwns[index] == -1) {
m_stringOwns[index] = own;
return true;
} else if (m_stringOwns[index] == own) {
return true;
} else {
return false;
}
}
public boolean touch(int index, int own) {
if (index < 0 || m_stringOwns == null || index >= m_stringOwns.length) {
return false;
}
if (m_stringOwns[index] == -1) {
m_stringOwns[index] = own;
return true;
} else if (m_stringOwns[index] == own) {
return true;
} else {
return false;
}
}
private int[] m_stringOffsets;
private byte[] m_strings;
private int[] m_styleOffsets;
private int[] m_styles;
private boolean m_isUTF8;
private int[] m_stringOwns;
private static final CharsetDecoder UTF16LE_DECODER =
Charset.forName("UTF-16LE").newDecoder();
private static final CharsetDecoder UTF8_DECODER =
Charset.forName("UTF-8").newDecoder();
private static final Logger LOGGER =
Logger.getLogger(StringBlock.class.getName());
private static final int CHUNK_TYPE = 0x001C0001;
private static final int UTF8_FLAG = 0x00000100;
private int[] m_stringOffsets;
private byte[] m_strings;
private int[] m_styleOffsets;
private int[] m_styles;
private boolean m_isUTF8;
private int[] m_stringOwns;
private static final CharsetDecoder UTF16LE_DECODER = Charset.forName(
"UTF-16LE").newDecoder();
private static final CharsetDecoder UTF8_DECODER = Charset.forName("UTF-8")
.newDecoder();
private static final Logger LOGGER = Logger.getLogger(StringBlock.class
.getName());
private static final int CHUNK_TYPE = 0x001C0001;
private static final int UTF8_FLAG = 0x00000100;
}

View File

@ -36,122 +36,133 @@ import brut.androlib.res.util.ExtXmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class XmlPullStreamDecoder implements ResStreamDecoder {
public XmlPullStreamDecoder(XmlPullParser parser,
ExtXmlSerializer serializer) {
this.mParser = parser;
this.mSerial = serializer;
}
public XmlPullStreamDecoder(XmlPullParser parser,
ExtXmlSerializer serializer) {
this.mParser = parser;
this.mSerial = serializer;
}
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
final ResTable resTable = ((AXmlResourceParser)mParser).getAttrDecoder().getCurrentPackage().getResTable();
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial, factory){
boolean hideSdkInfo = false;
boolean hidePackageInfo = false;
@Override
public void event(XmlPullParser pp) throws XmlPullParserException, IOException {
int type = pp.getEventType();
if (type == XmlPullParser.START_TAG) {
if ("manifest".equalsIgnoreCase(pp.getName())) {
try {
hidePackageInfo = parseManifest(pp);
} catch (AndrolibException e) {}
}else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
try {
hideSdkInfo = parseAttr(pp);
if(hideSdkInfo) {
return;
}
} catch (AndrolibException e) {}
}
} else if (hideSdkInfo && type == XmlPullParser.END_TAG &&
"uses-sdk".equalsIgnoreCase(pp.getName())) {
return;
} else if (hidePackageInfo && type == XmlPullParser.END_TAG &&
"manifest".equalsIgnoreCase(pp.getName())) {
super.event(pp);
return;
}
super.event(pp);
}
private boolean parseManifest(XmlPullParser pp) throws AndrolibException {
ResTable restable = resTable;
// read <manifest> for package:
for (int i = 0; i < pp.getAttributeCount(); i++) {
if (pp.getAttributeName(i).equalsIgnoreCase(("package"))) {
restable.addPackageInfo("orig_package", pp.getAttributeValue(i));
}
}
return true;
}
private boolean parseAttr(XmlPullParser pp) throws AndrolibException {
ResTable restable = resTable;
for (int i = 0; i < pp.getAttributeCount(); i++) {
final String a_ns = "http://schemas.android.com/apk/res/android";
String ns = pp.getAttributeNamespace (i);
if (a_ns.equalsIgnoreCase(ns)) {
String name = pp.getAttributeName (i);
String value = pp.getAttributeValue (i);
if (name != null && value != null) {
if (name.equalsIgnoreCase("minSdkVersion") ||
name.equalsIgnoreCase("targetSdkVersion") ||
name.equalsIgnoreCase("maxSdkVersion")) {
restable.addSdkInfo(name, value);
} else {
restable.clearSdkInfo();
return false;//Found unknown flags
}
}
} else {
resTable.clearSdkInfo();
if (i >= pp.getAttributeCount()) {
return false;//Found unknown flags
}
}
}
return true;
}
};
@Override
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
XmlPullWrapperFactory factory = XmlPullWrapperFactory.newInstance();
XmlPullParserWrapper par = factory.newPullParserWrapper(mParser);
final ResTable resTable = ((AXmlResourceParser) mParser)
.getAttrDecoder().getCurrentPackage().getResTable();
par.setInput(in, null);
ser.setOutput(out, null);
XmlSerializerWrapper ser = new StaticXmlSerializerWrapper(mSerial,
factory) {
boolean hideSdkInfo = false;
boolean hidePackageInfo = false;
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
ser.event(par);
}
ser.flush();
} catch (XmlPullParserException ex) {
throw new AndrolibException("Could not decode XML", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode XML", ex);
}
}
@Override
public void event(XmlPullParser pp)
throws XmlPullParserException, IOException {
int type = pp.getEventType();
public void decodeManifest(InputStream in, OutputStream out)
throws AndrolibException {
mOptimizeForManifest = true;
try {
decode(in, out);
} finally {
mOptimizeForManifest = false;
}
}
if (type == XmlPullParser.START_TAG) {
if ("manifest".equalsIgnoreCase(pp.getName())) {
try {
hidePackageInfo = parseManifest(pp);
} catch (AndrolibException e) {
}
} else if ("uses-sdk".equalsIgnoreCase(pp.getName())) {
try {
hideSdkInfo = parseAttr(pp);
if (hideSdkInfo) {
return;
}
} catch (AndrolibException e) {
}
}
} else if (hideSdkInfo && type == XmlPullParser.END_TAG
&& "uses-sdk".equalsIgnoreCase(pp.getName())) {
return;
} else if (hidePackageInfo && type == XmlPullParser.END_TAG
&& "manifest".equalsIgnoreCase(pp.getName())) {
super.event(pp);
return;
}
super.event(pp);
}
private final XmlPullParser mParser;
private final ExtXmlSerializer mSerial;
private boolean parseManifest(XmlPullParser pp)
throws AndrolibException {
ResTable restable = resTable;
private boolean mOptimizeForManifest = false;
// read <manifest> for package:
for (int i = 0; i < pp.getAttributeCount(); i++) {
if (pp.getAttributeName(i)
.equalsIgnoreCase(("package"))) {
restable.addPackageInfo("orig_package",
pp.getAttributeValue(i));
}
}
return true;
}
private final static Logger LOGGER =
Logger.getLogger(XmlPullStreamDecoder.class.getName());
private boolean parseAttr(XmlPullParser pp)
throws AndrolibException {
ResTable restable = resTable;
for (int i = 0; i < pp.getAttributeCount(); i++) {
final String a_ns = "http://schemas.android.com/apk/res/android";
String ns = pp.getAttributeNamespace(i);
if (a_ns.equalsIgnoreCase(ns)) {
String name = pp.getAttributeName(i);
String value = pp.getAttributeValue(i);
if (name != null && value != null) {
if (name.equalsIgnoreCase("minSdkVersion")
|| name.equalsIgnoreCase("targetSdkVersion")
|| name.equalsIgnoreCase("maxSdkVersion")) {
restable.addSdkInfo(name, value);
} else {
restable.clearSdkInfo();
return false;// Found unknown flags
}
}
} else {
resTable.clearSdkInfo();
if (i >= pp.getAttributeCount()) {
return false;// Found unknown flags
}
}
}
return true;
}
};
par.setInput(in, null);
ser.setOutput(out, null);
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
ser.event(par);
}
ser.flush();
} catch (XmlPullParserException ex) {
throw new AndrolibException("Could not decode XML", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode XML", ex);
}
}
public void decodeManifest(InputStream in, OutputStream out)
throws AndrolibException {
mOptimizeForManifest = true;
try {
decode(in, out);
} finally {
mOptimizeForManifest = false;
}
}
private final XmlPullParser mParser;
private final ExtXmlSerializer mSerial;
private boolean mOptimizeForManifest = false;
private final static Logger LOGGER = Logger
.getLogger(XmlPullStreamDecoder.class.getName());
}

View File

@ -27,37 +27,36 @@ import java.net.URI;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ExtFile extends File {
public ExtFile(File file) {
super(file.getPath());
}
public ExtFile(File file) {
super(file.getPath());
}
public ExtFile(URI uri) {
super(uri);
}
public ExtFile(URI uri) {
super(uri);
}
public ExtFile(File parent, String child) {
super(parent, child);
}
public ExtFile(File parent, String child) {
super(parent, child);
}
public ExtFile(String parent, String child) {
super(parent, child);
}
public ExtFile(String parent, String child) {
super(parent, child);
}
public ExtFile(String pathname) {
super(pathname);
}
public ExtFile(String pathname) {
super(pathname);
}
public Directory getDirectory() throws DirectoryException {
if (mDirectory == null) {
if (isDirectory()) {
mDirectory = new FileDirectory(this);
} else {
mDirectory = new ZipRODirectory(this);
}
}
return mDirectory;
}
public Directory getDirectory() throws DirectoryException {
if (mDirectory == null) {
if (isDirectory()) {
mDirectory = new FileDirectory(this);
} else {
mDirectory = new ZipRODirectory(this);
}
}
return mDirectory;
}
private Directory mDirectory;
private Directory mDirectory;
}

View File

@ -23,57 +23,59 @@ import org.xmlpull.mxp1_serializer.MXSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ExtMXSerializer extends MXSerializer implements ExtXmlSerializer {
@Override
public void startDocument(String encoding, Boolean standalone) throws
IOException, IllegalArgumentException, IllegalStateException {
super.startDocument(encoding != null ? encoding : mDefaultEncoding,
standalone);
this.newLine();
}
@Override
public void startDocument(String encoding, Boolean standalone)
throws IOException, IllegalArgumentException, IllegalStateException {
super.startDocument(encoding != null ? encoding : mDefaultEncoding,
standalone);
this.newLine();
}
@Override
protected void writeAttributeValue(String value, Writer out)
throws IOException {
if (mIsDisabledAttrEscape) {
out.write(value);
return;
}
super.writeAttributeValue(value, out);
}
@Override
protected void writeAttributeValue(String value, Writer out)
throws IOException {
if (mIsDisabledAttrEscape) {
out.write(value);
return;
}
super.writeAttributeValue(value, out);
}
@Override
public void setOutput(OutputStream os, String encoding) throws IOException {
super.setOutput(os, encoding != null ? encoding : mDefaultEncoding);
}
@Override
public void setOutput(OutputStream os, String encoding) throws IOException {
super.setOutput(os, encoding != null ? encoding : mDefaultEncoding);
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
return mDefaultEncoding;
}
return super.getProperty(name);
}
@Override
public Object getProperty(String name) throws IllegalArgumentException {
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
return mDefaultEncoding;
}
return super.getProperty(name);
}
@Override
public void setProperty(String name, Object value)
throws IllegalArgumentException, IllegalStateException {
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
mDefaultEncoding = (String) value;
} else {
super.setProperty(name, value);
}
}
@Override
public void setProperty(String name, Object value)
throws IllegalArgumentException, IllegalStateException {
if (PROPERTY_DEFAULT_ENCODING.equals(name)) {
mDefaultEncoding = (String) value;
} else {
super.setProperty(name, value);
}
}
public ExtXmlSerializer newLine() throws IOException {
super.out.write(lineSeparator);
return this;
}
@Override
public ExtXmlSerializer newLine() throws IOException {
super.out.write(lineSeparator);
return this;
}
public void setDisabledAttrEscape(boolean disabled) {
mIsDisabledAttrEscape = disabled;
}
@Override
public void setDisabledAttrEscape(boolean disabled) {
mIsDisabledAttrEscape = disabled;
}
private String mDefaultEncoding;
private boolean mIsDisabledAttrEscape = false;
private String mDefaultEncoding;
private boolean mIsDisabledAttrEscape = false;
}

View File

@ -24,12 +24,11 @@ import org.xmlpull.v1.XmlSerializer;
*/
public interface ExtXmlSerializer extends XmlSerializer {
public ExtXmlSerializer newLine() throws IOException;
public void setDisabledAttrEscape(boolean disabled);
public ExtXmlSerializer newLine() throws IOException;
public static final String PROPERTY_SERIALIZER_INDENTATION =
"http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR =
"http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
public void setDisabledAttrEscape(boolean disabled);
public static final String PROPERTY_SERIALIZER_INDENTATION = "http://xmlpull.org/v1/doc/properties.html#serializer-indentation";
public static final String PROPERTY_SERIALIZER_LINE_SEPARATOR = "http://xmlpull.org/v1/doc/properties.html#serializer-line-separator";
public static final String PROPERTY_DEFAULT_ENCODING = "DEFAULT_ENCODING";
}

View File

@ -25,6 +25,6 @@ import org.xmlpull.v1.XmlSerializer;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResValuesXmlSerializable {
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException;
public void serializeToResValuesXml(XmlSerializer serializer,
ResResource res) throws IOException, AndrolibException;
}

View File

@ -22,6 +22,7 @@ import brut.androlib.AndrolibException;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResXmlEncodable {
public String encodeAsResXmlAttr() throws AndrolibException;
public String encodeAsResXmlValue() throws AndrolibException;
public String encodeAsResXmlAttr() throws AndrolibException;
public String encodeAsResXmlValue() throws AndrolibException;
}

View File

@ -25,178 +25,176 @@ import java.util.List;
*/
public final class ResXmlEncoders {
public static String escapeXmlChars(String str) {
return str.replace("&", "&amp;").replace("<", "&lt;");
}
public static String escapeXmlChars(String str) {
return str.replace("&", "&amp;").replace("<", "&lt;");
}
public static String encodeAsResXmlAttr(String str) {
if (str.isEmpty()) {
return str;
}
public static String encodeAsResXmlAttr(String str) {
if (str.isEmpty()) {
return str;
}
char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10);
char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10);
switch (chars[0]) {
case '#':
case '@':
case '?':
out.append('\\');
}
switch (chars[0]) {
case '#':
case '@':
case '?':
out.append('\\');
}
for (char c : chars) {
switch (c) {
case '\\':
out.append('\\');
break;
case '"':
out.append("&quot;");
continue;
case '\n':
out.append("\\n");
continue;
default:
if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c));
continue;
}
}
out.append(c);
}
for (char c : chars) {
switch (c) {
case '\\':
out.append('\\');
break;
case '"':
out.append("&quot;");
continue;
case '\n':
out.append("\\n");
continue;
default:
if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c));
continue;
}
}
out.append(c);
}
return out.toString();
}
return out.toString();
}
public static String encodeAsXmlValue(String str) {
if (str.isEmpty()) {
return str;
}
public static String encodeAsXmlValue(String str) {
if (str.isEmpty()) {
return str;
}
char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10);
char[] chars = str.toCharArray();
StringBuilder out = new StringBuilder(str.length() + 10);
switch (chars[0]) {
case '#':
case '@':
case '?':
out.append('\\');
}
switch (chars[0]) {
case '#':
case '@':
case '?':
out.append('\\');
}
boolean isInStyleTag = false;
int startPos = 0;
boolean enclose = false;
boolean wasSpace = true;
for (char c : chars) {
if (isInStyleTag) {
if (c == '>') {
isInStyleTag = false;
startPos = out.length() + 1;
enclose = false;
}
} else if (c == ' ') {
if (wasSpace) {
enclose = true;
}
wasSpace = true;
} else {
wasSpace = false;
switch (c) {
case '\\':
out.append('\\');
break;
case '\'':
case '\n':
enclose = true;
break;
case '"':
out.append('\\');
break;
case '<':
isInStyleTag = true;
if (enclose) {
out.insert(startPos, '"').append('"');
}
break;
default:
if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c));
continue;
}
}
}
out.append(c);
}
boolean isInStyleTag = false;
int startPos = 0;
boolean enclose = false;
boolean wasSpace = true;
for (char c : chars) {
if (isInStyleTag) {
if (c == '>') {
isInStyleTag = false;
startPos = out.length() + 1;
enclose = false;
}
} else if (c == ' ') {
if (wasSpace) {
enclose = true;
}
wasSpace = true;
} else {
wasSpace = false;
switch (c) {
case '\\':
out.append('\\');
break;
case '\'':
case '\n':
enclose = true;
break;
case '"':
out.append('\\');
break;
case '<':
isInStyleTag = true;
if (enclose) {
out.insert(startPos, '"').append('"');
}
break;
default:
if (!isPrintableChar(c)) {
out.append(String.format("\\u%04x", (int) c));
continue;
}
}
}
out.append(c);
}
if (enclose || wasSpace) {
out.insert(startPos, '"').append('"');
}
if (enclose || wasSpace) {
out.insert(startPos, '"').append('"');
}
return out.toString();
}
return out.toString();
}
public static boolean hasMultipleNonPositionalSubstitutions(String str) {
return findNonPositionalSubstitutions(str, 2).size() > 1;
}
public static boolean hasMultipleNonPositionalSubstitutions(String str) {
return findNonPositionalSubstitutions(str, 2).size() > 1;
}
public static String enumerateNonPositionalSubstitutions(String str) {
List<Integer> subs = findNonPositionalSubstitutions(str, -1);
if (subs.size() < 2) {
return str;
}
public static String enumerateNonPositionalSubstitutions(String str) {
List<Integer> subs = findNonPositionalSubstitutions(str, -1);
if (subs.size() < 2) {
return str;
}
StringBuilder out = new StringBuilder();
int pos = 0;
int count = 0;
for (Integer sub : subs) {
out.append(str.substring(pos, ++sub)).append(++count).append('$');
pos = sub;
}
out.append(str.substring(pos));
StringBuilder out = new StringBuilder();
int pos = 0;
int count = 0;
for (Integer sub : subs) {
out.append(str.substring(pos, ++sub)).append(++count).append('$');
pos = sub;
}
out.append(str.substring(pos));
return out.toString();
}
return out.toString();
}
/**
* It searches for "%", but not "%%" nor "%(\d)+\$"
*/
private static List<Integer> findNonPositionalSubstitutions(String str,
int max) {
int pos = 0;
int pos2 = 0;
int count = 0;
int length = str.length();
List<Integer> ret = new ArrayList<Integer>();
while((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) {
if (pos2 == length) {
break;
}
char c = str.charAt(pos2++);
if (c == '%') {
continue;
}
if (c >= '0' && c <= '9' && pos2 < length) {
do {
c = str.charAt(pos2++);
} while (c >= '0' && c <= '9' && pos2 < length);
if (c == '$') {
continue;
}
}
/**
* It searches for "%", but not "%%" nor "%(\d)+\$"
*/
private static List<Integer> findNonPositionalSubstitutions(String str,
int max) {
int pos = 0;
int pos2 = 0;
int count = 0;
int length = str.length();
List<Integer> ret = new ArrayList<Integer>();
while ((pos2 = (pos = str.indexOf('%', pos2)) + 1) != 0) {
if (pos2 == length) {
break;
}
char c = str.charAt(pos2++);
if (c == '%') {
continue;
}
if (c >= '0' && c <= '9' && pos2 < length) {
do {
c = str.charAt(pos2++);
} while (c >= '0' && c <= '9' && pos2 < length);
if (c == '$') {
continue;
}
}
ret.add(pos);
if (max != -1 && ++count >= max) {
break;
}
}
ret.add(pos);
if (max != -1 && ++count >= max) {
break;
}
}
return ret;
}
return ret;
}
private static boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return !Character.isISOControl(c)
&& c != KeyEvent.CHAR_UNDEFINED
&& block != null
&& block != Character.UnicodeBlock.SPECIALS;
}
private static boolean isPrintableChar(char c) {
Character.UnicodeBlock block = Character.UnicodeBlock.of(c);
return !Character.isISOControl(c) && c != KeyEvent.CHAR_UNDEFINED
&& block != null && block != Character.UnicodeBlock.SPECIALS;
}
}

View File

@ -21,207 +21,197 @@ import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.jf.dexlib.Code.Analysis.RegisterType;
import org.jf.dexlib.Code.Opcode;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class DebugInjector {
public static void inject(ListIterator<String> it, StringBuilder out)
throws AndrolibException {
new DebugInjector(it, out).inject();
}
public static void inject(ListIterator<String> it, StringBuilder out)
throws AndrolibException {
new DebugInjector(it, out).inject();
}
private DebugInjector(ListIterator<String> it, StringBuilder out) {
mIt = it;
mOut = out;
}
private DebugInjector(ListIterator<String> it, StringBuilder out) {
mIt = it;
mOut = out;
}
private void inject() throws AndrolibException {
String definition = nextAndAppend();
if (
definition.contains(" abstract ") ||
definition.contains(" native ")
) {
nextAndAppend();
return;
}
injectParameters(definition);
private void inject() throws AndrolibException {
String definition = nextAndAppend();
if (definition.contains(" abstract ")
|| definition.contains(" native ")) {
nextAndAppend();
return;
}
injectParameters(definition);
boolean end = false;
while (!end) {
end = step();
}
}
boolean end = false;
while (!end) {
end = step();
}
}
private void injectParameters(String definition) throws AndrolibException {
int pos = definition.indexOf('(');
if (pos == -1) {
throw new AndrolibException();
}
int pos2 = definition.indexOf(')', pos);
if (pos2 == -1) {
throw new AndrolibException();
}
String params = definition.substring(pos + 1, pos2);
private void injectParameters(String definition) throws AndrolibException {
int pos = definition.indexOf('(');
if (pos == -1) {
throw new AndrolibException();
}
int pos2 = definition.indexOf(')', pos);
if (pos2 == -1) {
throw new AndrolibException();
}
String params = definition.substring(pos + 1, pos2);
int i = definition.contains(" static ") ? 0 : 1;
int argc = TypeName.listFromInternalName(params).size() + i;
while(i < argc) {
mOut.append(".parameter \"p").append(i).append("\"\n");
i++;
}
}
int i = definition.contains(" static ") ? 0 : 1;
int argc = TypeName.listFromInternalName(params).size() + i;
while (i < argc) {
mOut.append(".parameter \"p").append(i).append("\"\n");
i++;
}
}
private boolean step() {
String line = next();
if (line.isEmpty()) {
return false;
}
private boolean step() {
String line = next();
if (line.isEmpty()) {
return false;
}
switch (line.charAt(0)) {
case '#':
return processComment(line);
case ':':
append(line);
return false;
case '.':
return processDirective(line);
default:
return processInstruction(line);
}
}
switch (line.charAt(0)) {
case '#':
return processComment(line);
case ':':
append(line);
return false;
case '.':
return processDirective(line);
default:
return processInstruction(line);
}
}
private boolean processComment(String line) {
if (mFirstInstruction) {
return false;
}
private boolean processComment(String line) {
if (mFirstInstruction) {
return false;
}
Matcher m = REGISTER_INFO_PATTERN.matcher(line);
Matcher m = REGISTER_INFO_PATTERN.matcher(line);
while (m.find()) {
String localName = m.group(1);
String localType = null;
switch (RegisterType.Category.valueOf(m.group(2))) {
case Reference:
case Null:
case UninitRef:
case UninitThis:
localType = "Ljava/lang/Object;";
break;
case Boolean:
localType = "Z";
break;
case Integer:
case One:
case Unknown:
localType = "I";
break;
case Uninit:
case Conflicted:
if (mInitializedRegisters.remove(localName)) {
mOut.append(".end local ").append(localName)
.append('\n');
}
continue;
case Short:
case PosShort:
localType = "S";
break;
case Byte:
case PosByte:
localType = "B";
break;
case Char:
localType = "C";
break;
case Float:
localType = "F";
break;
case LongHi:
case LongLo:
localType = "J";
break;
case DoubleHi:
case DoubleLo:
localType = "D";
break;
default:
assert false;
}
while (m.find()) {
String localName = m.group(1);
String localType = null;
switch (RegisterType.Category.valueOf(m.group(2))) {
case Reference:
case Null:
case UninitRef:
case UninitThis:
localType = "Ljava/lang/Object;";
break;
case Boolean:
localType = "Z";
break;
case Integer:
case One:
case Unknown:
localType = "I";
break;
case Uninit:
case Conflicted:
if (mInitializedRegisters.remove(localName)) {
mOut.append(".end local ").append(localName).append('\n');
}
continue;
case Short:
case PosShort:
localType = "S";
break;
case Byte:
case PosByte:
localType = "B";
break;
case Char:
localType = "C";
break;
case Float:
localType = "F";
break;
case LongHi:
case LongLo:
localType = "J";
break;
case DoubleHi:
case DoubleLo:
localType = "D";
break;
default:
assert false;
}
mInitializedRegisters.add(localName);
mOut.append(".local ").append(localName).append(", ")
.append(localName).append(':').append(localType).append('\n');
}
mInitializedRegisters.add(localName);
mOut.append(".local ").append(localName).append(", ")
.append(localName).append(':').append(localType)
.append('\n');
}
return false;
}
return false;
}
private boolean processDirective(String line) {
String line2 = line.substring(1);
if (
line2.startsWith("line ") ||
line2.equals("prologue") ||
line2.startsWith("parameter") ||
line2.startsWith("local ") ||
line2.startsWith("end local ")
) {
return false;
}
private boolean processDirective(String line) {
String line2 = line.substring(1);
if (line2.startsWith("line ") || line2.equals("prologue")
|| line2.startsWith("parameter") || line2.startsWith("local ")
|| line2.startsWith("end local ")) {
return false;
}
append(line);
if (line2.equals("end method")) {
return true;
}
if (
line2.startsWith("annotation ") ||
line2.equals("sparse-switch") ||
line2.startsWith("packed-switch ") ||
line2.startsWith("array-data ")
) {
while(true) {
line2 = nextAndAppend();
if (line2.startsWith(".end ")) {
break;
}
}
}
return false;
}
append(line);
if (line2.equals("end method")) {
return true;
}
if (line2.startsWith("annotation ") || line2.equals("sparse-switch")
|| line2.startsWith("packed-switch ")
|| line2.startsWith("array-data ")) {
while (true) {
line2 = nextAndAppend();
if (line2.startsWith(".end ")) {
break;
}
}
}
return false;
}
private boolean processInstruction(String line) {
if (mFirstInstruction) {
mOut.append(".prologue\n");
mFirstInstruction = false;
}
mOut.append(".line ").append(mIt.nextIndex()).append('\n')
.append(line).append('\n');
private boolean processInstruction(String line) {
if (mFirstInstruction) {
mOut.append(".prologue\n");
mFirstInstruction = false;
}
mOut.append(".line ").append(mIt.nextIndex()).append('\n').append(line)
.append('\n');
return false;
}
return false;
}
private String next() {
return mIt.next().trim();
}
private String next() {
return mIt.next().trim();
}
private String nextAndAppend() {
String line = next();
append(line);
return line;
}
private String nextAndAppend() {
String line = next();
append(line);
return line;
}
private void append(String append) {
mOut.append(append).append('\n');
}
private void append(String append) {
mOut.append(append).append('\n');
}
private final ListIterator<String> mIt;
private final StringBuilder mOut;
private final ListIterator<String> mIt;
private final StringBuilder mOut;
private boolean mFirstInstruction = true;
private final Set<String> mInitializedRegisters = new HashSet<String>();
private boolean mFirstInstruction = true;
private final Set<String> mInitializedRegisters = new HashSet<String>();
private static final Pattern REGISTER_INFO_PATTERN =
Pattern.compile("((?:p|v)\\d+)=\\(([^)]+)\\);");
private static final Pattern REGISTER_INFO_PATTERN = Pattern
.compile("((?:p|v)\\d+)=\\(([^)]+)\\);");
}

View File

@ -28,58 +28,57 @@ import org.jf.dexlib.Util.ByteArrayAnnotatedOutput;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class DexFileBuilder {
public void addSmaliFile(File smaliFile) throws AndrolibException {
try {
addSmaliFile(new FileInputStream(smaliFile),
smaliFile.getAbsolutePath());
} catch (FileNotFoundException ex) {
throw new AndrolibException(ex);
}
}
public void addSmaliFile(File smaliFile) throws AndrolibException {
try {
addSmaliFile(new FileInputStream(smaliFile),
smaliFile.getAbsolutePath());
} catch (FileNotFoundException ex) {
throw new AndrolibException(ex);
}
}
public void addSmaliFile(InputStream smaliStream, String name)
throws AndrolibException {
try {
if (! SmaliMod.assembleSmaliFile(
smaliStream, name, mDexFile, false, false, false)) {
throw new AndrolibException(
"Could not smali file: " + name);
}
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (RecognitionException ex) {
throw new AndrolibException(ex);
}
}
public void addSmaliFile(InputStream smaliStream, String name)
throws AndrolibException {
try {
if (!SmaliMod.assembleSmaliFile(smaliStream, name, mDexFile, false,
false, false)) {
throw new AndrolibException("Could not smali file: " + name);
}
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (RecognitionException ex) {
throw new AndrolibException(ex);
}
}
public void writeTo(File dexFile) throws AndrolibException {
try {
OutputStream out = new FileOutputStream(dexFile);
out.write(getAsByteArray());
out.close();
} catch (IOException ex) {
throw new AndrolibException(
"Could not write dex to file: " + dexFile, ex);
}
}
public void writeTo(File dexFile) throws AndrolibException {
try {
OutputStream out = new FileOutputStream(dexFile);
out.write(getAsByteArray());
out.close();
} catch (IOException ex) {
throw new AndrolibException("Could not write dex to file: "
+ dexFile, ex);
}
}
public byte[] getAsByteArray() {
mDexFile.place();
for (CodeItem codeItem: mDexFile.CodeItemsSection.getItems()) {
codeItem.fixInstructions(true, true);
}
public byte[] getAsByteArray() {
mDexFile.place();
for (CodeItem codeItem : mDexFile.CodeItemsSection.getItems()) {
codeItem.fixInstructions(true, true);
}
mDexFile.place();
mDexFile.place();
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
mDexFile.writeTo(out);
byte[] bytes = out.toByteArray();
ByteArrayAnnotatedOutput out = new ByteArrayAnnotatedOutput();
mDexFile.writeTo(out);
byte[] bytes = out.toByteArray();
DexFile.calcSignature(bytes);
DexFile.calcChecksum(bytes);
DexFile.calcSignature(bytes);
DexFile.calcChecksum(bytes);
return bytes;
}
return bytes;
}
private final DexFile mDexFile = new DexFile();
private final DexFile mDexFile = new DexFile();
}

View File

@ -31,86 +31,85 @@ import org.apache.commons.io.IOUtils;
*/
public class SmaliBuilder {
public static void build(ExtFile smaliDir, File dexFile,
HashMap<String, Boolean> flags)
throws AndrolibException {
new SmaliBuilder(smaliDir, dexFile, flags).build();
}
public static void build(ExtFile smaliDir, File dexFile,
HashMap<String, Boolean> flags) throws AndrolibException {
new SmaliBuilder(smaliDir, dexFile, flags).build();
}
private SmaliBuilder(ExtFile smaliDir, File dexFile, HashMap<String, Boolean> flags) {
mSmaliDir = smaliDir;
mDexFile = dexFile;
mFlags = flags;
}
private SmaliBuilder(ExtFile smaliDir, File dexFile,
HashMap<String, Boolean> flags) {
mSmaliDir = smaliDir;
mDexFile = dexFile;
mFlags = flags;
}
private void build() throws AndrolibException {
try {
mDexBuilder = new DexFileBuilder();
for (String fileName : mSmaliDir.getDirectory().getFiles(true)) {
buildFile(fileName);
}
mDexBuilder.writeTo(mDexFile);
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
private void build() throws AndrolibException {
try {
mDexBuilder = new DexFileBuilder();
for (String fileName : mSmaliDir.getDirectory().getFiles(true)) {
buildFile(fileName);
}
mDexBuilder.writeTo(mDexFile);
} catch (IOException ex) {
throw new AndrolibException(ex);
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
}
private void buildFile(String fileName) throws AndrolibException,
IOException {
File inFile = new File(mSmaliDir, fileName);
InputStream inStream = new FileInputStream(inFile);
private void buildFile(String fileName) throws AndrolibException,
IOException {
File inFile = new File(mSmaliDir, fileName);
InputStream inStream = new FileInputStream(inFile);
if (fileName.endsWith(".smali")) {
mDexBuilder.addSmaliFile(inFile);
return;
}
if (! fileName.endsWith(".java")) {
LOGGER.warning("Unknown file type, ignoring: " + inFile);
return;
}
if (fileName.endsWith(".smali")) {
mDexBuilder.addSmaliFile(inFile);
return;
}
if (!fileName.endsWith(".java")) {
LOGGER.warning("Unknown file type, ignoring: " + inFile);
return;
}
StringBuilder out = new StringBuilder();
List<String> lines = IOUtils.readLines(inStream);
StringBuilder out = new StringBuilder();
List<String> lines = IOUtils.readLines(inStream);
if (!mFlags.containsKey("debug")) {
final String[] linesArray = lines.toArray(new String[0]);
for (int i = 2; i < linesArray.length - 2; i++) {
out.append(linesArray[i]).append('\n');
}
} else {
lines.remove(lines.size() - 1);
lines.remove(lines.size() - 1);
ListIterator<String> it = lines.listIterator(2);
if (!mFlags.containsKey("debug")) {
final String[] linesArray = lines.toArray(new String[0]);
for (int i = 2; i < linesArray.length - 2; i++) {
out.append(linesArray[i]).append('\n');
}
} else {
lines.remove(lines.size() - 1);
lines.remove(lines.size() - 1);
ListIterator<String> it = lines.listIterator(2);
out.append(".source \"").append(inFile.getName()).append("\"\n");
while (it.hasNext()) {
String line = it.next().trim();
if (line.isEmpty() || line.charAt(0) == '#' ||
line.startsWith(".source")) {
continue;
}
if (line.startsWith(".method ")) {
it.previous();
DebugInjector.inject(it, out);
continue;
}
out.append(".source \"").append(inFile.getName()).append("\"\n");
while (it.hasNext()) {
String line = it.next().trim();
if (line.isEmpty() || line.charAt(0) == '#'
|| line.startsWith(".source")) {
continue;
}
if (line.startsWith(".method ")) {
it.previous();
DebugInjector.inject(it, out);
continue;
}
out.append(line).append('\n');
}
}
mDexBuilder.addSmaliFile(
IOUtils.toInputStream(out.toString()), fileName);
}
out.append(line).append('\n');
}
}
mDexBuilder.addSmaliFile(IOUtils.toInputStream(out.toString()),
fileName);
}
private final ExtFile mSmaliDir;
private final File mDexFile;
private final HashMap<String, Boolean> mFlags;
private final ExtFile mSmaliDir;
private final File mDexFile;
private final HashMap<String, Boolean> mFlags;
private DexFileBuilder mDexBuilder;
private DexFileBuilder mDexBuilder;
private final static Logger LOGGER =
Logger.getLogger(SmaliBuilder.class.getName());
private final static Logger LOGGER = Logger.getLogger(SmaliBuilder.class
.getName());
}

View File

@ -29,34 +29,35 @@ import org.jf.dexlib.DexFile;
*/
public class SmaliDecoder {
public static void decode(File apkFile, File outDir, boolean debug, boolean bakdeb)
throws AndrolibException {
new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode();
}
public static void decode(File apkFile, File outDir, boolean debug,
boolean bakdeb) throws AndrolibException {
new SmaliDecoder(apkFile, outDir, debug, bakdeb).decode();
}
private SmaliDecoder(File apkFile, File outDir, boolean debug, boolean bakdeb) {
mApkFile = apkFile;
mOutDir = outDir;
mDebug = debug;
mBakDeb = bakdeb;
}
private SmaliDecoder(File apkFile, File outDir, boolean debug,
boolean bakdeb) {
mApkFile = apkFile;
mOutDir = outDir;
mDebug = debug;
mBakDeb = bakdeb;
}
private void decode() throws AndrolibException {
if (mDebug) {
ClassPath.dontLoadClassPath = true;
}
try {
baksmali.disassembleDexFile(mApkFile.getAbsolutePath(),
new DexFile(mApkFile), false, mOutDir.getAbsolutePath(), null,
null, null, false, true, true, mBakDeb, false, false,
mDebug ? main.DIFFPRE: 0, false, false, null, false);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
private void decode() throws AndrolibException {
if (mDebug) {
ClassPath.dontLoadClassPath = true;
}
try {
baksmali.disassembleDexFile(mApkFile.getAbsolutePath(),
new DexFile(mApkFile), false, mOutDir.getAbsolutePath(),
null, null, null, false, true, true, mBakDeb, false, false,
mDebug ? main.DIFFPRE : 0, false, false, null, false);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
private final File mApkFile;
private final File mOutDir;
private final boolean mDebug;
private final boolean mBakDeb;
private final File mApkFile;
private final File mOutDir;
private final boolean mDebug;
private final boolean mBakDeb;
}

View File

@ -26,184 +26,182 @@ import java.util.List;
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class TypeName {
public final String package_;
public final String type;
public final String innerType;
public final int array;
public final String package_;
public final String type;
public final String innerType;
public final int array;
public TypeName(String type, int array) {
this(null, type, null, array);
}
public TypeName(String type, int array) {
this(null, type, null, array);
}
public TypeName(String package_, String type, String innerType, int array) {
this.package_ = package_;
this.type = type;
this.innerType = innerType;
this.array = array;
}
public TypeName(String package_, String type, String innerType, int array) {
this.package_ = package_;
this.type = type;
this.innerType = innerType;
this.array = array;
}
public String getShortenedName() {
return getName("java.lang".equals(package_), isFileOwner());
}
public String getShortenedName() {
return getName("java.lang".equals(package_), isFileOwner());
}
public String getName() {
return getName(false, false);
}
public String getName() {
return getName(false, false);
}
public String getName(boolean excludePackage, boolean separateInner) {
String name =
(package_ == null || excludePackage ? "" : package_ + '.') +
type +
(innerType != null ? (separateInner ? '$' : '.') + innerType : "");
for (int i = 0; i < array; i++) {
name += "[]";
}
return name;
}
public String getName(boolean excludePackage, boolean separateInner) {
String name = (package_ == null || excludePackage ? "" : package_ + '.')
+ type
+ (innerType != null ? (separateInner ? '$' : '.') + innerType
: "");
for (int i = 0; i < array; i++) {
name += "[]";
}
return name;
}
public String getJavaFilePath() {
return getFilePath(isFileOwner()) + ".java";
}
public String getJavaFilePath() {
return getFilePath(isFileOwner()) + ".java";
}
public String getSmaliFilePath() {
return getFilePath(true) + ".smali";
}
public String getSmaliFilePath() {
return getFilePath(true) + ".smali";
}
public String getFilePath(boolean separateInner) {
return package_.replace('.', File.separatorChar) + File.separatorChar
+ type + (separateInner && isInner() ? "$" + innerType : "");
}
public String getFilePath(boolean separateInner) {
return package_.replace('.', File.separatorChar) + File.separatorChar
+ type + (separateInner && isInner() ? "$" + innerType : "");
}
public boolean isInner() {
return innerType != null;
}
public boolean isInner() {
return innerType != null;
}
public boolean isArray() {
return array != 0;
}
public boolean isArray() {
return array != 0;
}
public boolean isFileOwner() {
if (mIsFileOwner == null) {
mIsFileOwner = true;
if (isInner()) {
char c = innerType.charAt(0);
if (c < '0' || c > '9') {
mIsFileOwner = false;
}
}
}
return mIsFileOwner;
}
public boolean isFileOwner() {
if (mIsFileOwner == null) {
mIsFileOwner = true;
if (isInner()) {
char c = innerType.charAt(0);
if (c < '0' || c > '9') {
mIsFileOwner = false;
}
}
}
return mIsFileOwner;
}
@Override
public String toString() {
return getName();
}
@Override
public String toString() {
return getName();
}
public static TypeName fromInternalName(String internal)
throws AndrolibException {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
if (duo.m2 != internal.length()) {
throw new AndrolibException(
"Invalid internal name: " + internal);
}
return duo.m1;
}
public static TypeName fromInternalName(String internal)
throws AndrolibException {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
if (duo.m2 != internal.length()) {
throw new AndrolibException("Invalid internal name: " + internal);
}
return duo.m1;
}
public static List<TypeName> listFromInternalName(String internal)
throws AndrolibException {
List<TypeName> types = new ArrayList<TypeName>();
while (! internal.isEmpty()) {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
types.add(duo.m1);
internal = internal.substring(duo.m2);
}
return types;
}
public static List<TypeName> listFromInternalName(String internal)
throws AndrolibException {
List<TypeName> types = new ArrayList<TypeName>();
while (!internal.isEmpty()) {
Duo<TypeName, Integer> duo = fetchFromInternalName(internal);
types.add(duo.m1);
internal = internal.substring(duo.m2);
}
return types;
}
public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
throws AndrolibException {
String origInternal = internal;
int array = 0;
public static Duo<TypeName, Integer> fetchFromInternalName(String internal)
throws AndrolibException {
String origInternal = internal;
int array = 0;
boolean isArray = false;
do {
if (internal.isEmpty()) {
throw new AndrolibException(
"Invalid internal name: " + origInternal);
}
isArray = internal.charAt(0) == '[';
if (isArray) {
array++;
internal = internal.substring(1);
}
} while (isArray);
boolean isArray = false;
do {
if (internal.isEmpty()) {
throw new AndrolibException("Invalid internal name: "
+ origInternal);
}
isArray = internal.charAt(0) == '[';
if (isArray) {
array++;
internal = internal.substring(1);
}
} while (isArray);
int length = array + 1;
String package_ = null;
String type = null;
String innerType = null;
switch (internal.charAt(0)) {
case 'B':
type = "byte";
break;
case 'C':
type = "char";
break;
case 'D':
type = "double";
break;
case 'F':
type = "float";
break;
case 'I':
type = "int";
break;
case 'J':
type = "long";
break;
case 'S':
type = "short";
break;
case 'Z':
type = "boolean";
break;
case 'V':
type = "void";
break;
case 'L':
int pos = internal.indexOf(';');
if (pos == -1) {
throw new AndrolibException(
"Invalid internal name: " + origInternal);
}
length += pos;
internal = internal.substring(1, pos);
int length = array + 1;
String package_ = null;
String type = null;
String innerType = null;
switch (internal.charAt(0)) {
case 'B':
type = "byte";
break;
case 'C':
type = "char";
break;
case 'D':
type = "double";
break;
case 'F':
type = "float";
break;
case 'I':
type = "int";
break;
case 'J':
type = "long";
break;
case 'S':
type = "short";
break;
case 'Z':
type = "boolean";
break;
case 'V':
type = "void";
break;
case 'L':
int pos = internal.indexOf(';');
if (pos == -1) {
throw new AndrolibException("Invalid internal name: "
+ origInternal);
}
length += pos;
internal = internal.substring(1, pos);
pos = internal.lastIndexOf('/');
if (pos == -1) {
package_ = "";
type = internal;
} else {
package_ = internal.substring(0, pos).replace('/', '.');
type = internal.substring(pos + 1);
}
pos = internal.lastIndexOf('/');
if (pos == -1) {
package_ = "";
type = internal;
} else {
package_ = internal.substring(0, pos).replace('/', '.');
type = internal.substring(pos + 1);
}
pos = type.indexOf('$');
if (pos != -1) {
innerType = type.substring(pos + 1);
type = type.substring(0, pos);
}
break;
default:
throw new AndrolibException(
"Invalid internal name: " + origInternal);
}
pos = type.indexOf('$');
if (pos != -1) {
innerType = type.substring(pos + 1);
type = type.substring(0, pos);
}
break;
default:
throw new AndrolibException("Invalid internal name: "
+ origInternal);
}
return new Duo<TypeName, Integer>(
new TypeName(package_, type, innerType, array), length);
}
return new Duo<TypeName, Integer>(new TypeName(package_, type,
innerType, array), length);
}
private Boolean mIsFileOwner;
private Boolean mIsFileOwner;
}

View File

@ -25,295 +25,306 @@ import java.io.InputStream;
/**
* Little-Endian version of DataInputStream.
* <p/>
* Very similar to DataInputStream except it reads
* little-endian instead of big-endian binary data. We can't extend
* DataInputStream directly since it has only final methods, though
* DataInputStream itself is not final. This forces us implement
* LEDataInputStream with a DataInputStream object, and use wrapper methods.
*
* Very similar to DataInputStream except it reads little-endian instead of
* big-endian binary data. We can't extend DataInputStream directly since it has
* only final methods, though DataInputStream itself is not final. This forces
* us implement LEDataInputStream with a DataInputStream object, and use wrapper
* methods.
*
* @author Roedy Green, Canadian Mind Products
* @version 1.8 2007-05-24
* @since 1998
*/
public final class LEDataInputStream implements DataInput
{
// ------------------------------ CONSTANTS ------------------------------
public final class LEDataInputStream implements DataInput {
// ------------------------------ CONSTANTS ------------------------------
/**
* undisplayed copyright notice.
*
* @noinspection UnusedDeclaration
*/
private static final String EMBEDDED_COPYRIGHT =
"copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com";
/**
* undisplayed copyright notice.
*
* @noinspection UnusedDeclaration
*/
private static final String EMBEDDED_COPYRIGHT = "copyright (c) 1999-2010 Roedy Green, Canadian Mind Products, http://mindprod.com";
// ------------------------------ FIELDS ------------------------------
// ------------------------------ FIELDS ------------------------------
/**
* to get at the big-Endian methods of a basic DataInputStream
*
* @noinspection WeakerAccess
*/
protected final DataInputStream dis;
/**
* to get at the big-Endian methods of a basic DataInputStream
*
* @noinspection WeakerAccess
*/
protected final DataInputStream dis;
/**
* to get at the a basic readBytes method.
*
* @noinspection WeakerAccess
*/
protected final InputStream is;
/**
* to get at the a basic readBytes method.
*
* @noinspection WeakerAccess
*/
protected final InputStream is;
/**
* work array for buffering input.
*
* @noinspection WeakerAccess
*/
protected final byte[] work;
// -------------------------- PUBLIC STATIC METHODS --------------------------
/**
* work array for buffering input.
*
* @noinspection WeakerAccess
*/
protected final byte[] work;
/**
* Note. This is a STATIC method!
*
* @param in stream to read UTF chars from (endian irrelevant)
*
* @return string from stream
* @throws IOException if read fails.
*/
public static String readUTF( DataInput in ) throws IOException
{
return DataInputStream.readUTF( in );
}
// -------------------------- PUBLIC STATIC METHODS
// --------------------------
// -------------------------- PUBLIC INSTANCE METHODS --------------------------
/**
* Note. This is a STATIC method!
*
* @param in
* stream to read UTF chars from (endian irrelevant)
*
* @return string from stream
* @throws IOException
* if read fails.
*/
public static String readUTF(DataInput in) throws IOException {
return DataInputStream.readUTF(in);
}
/**
* constructor.
*
* @param in binary inputstream of little-endian data.
*/
public LEDataInputStream( InputStream in )
{
this.is = in;
this.dis = new DataInputStream( in );
work = new byte[8];
}
// -------------------------- PUBLIC INSTANCE METHODS
// --------------------------
/**
* close.
*
* @throws IOException if close fails.
*/
public final void close() throws IOException
{
dis.close();
}
/**
* constructor.
*
* @param in
* binary inputstream of little-endian data.
*/
public LEDataInputStream(InputStream in) {
this.is = in;
this.dis = new DataInputStream(in);
work = new byte[8];
}
/**
* Read bytes. Watch out, read may return fewer bytes than requested.
*
* @param ba where the bytes go.
* @param off offset in buffer, not offset in file.
* @param len count of bytes to read.
*
* @return how many bytes read.
* @throws IOException if read fails.
*/
public final int read( byte ba[], int off, int len ) throws IOException
{
// For efficiency, we avoid one layer of wrapper
return is.read( ba, off, len );
}
/**
* close.
*
* @throws IOException
* if close fails.
*/
public final void close() throws IOException {
dis.close();
}
/**
* read only a one-byte boolean.
*
* @return true or false.
* @throws IOException if read fails.
* @see java.io.DataInput#readBoolean()
*/
public final boolean readBoolean() throws IOException
{
return dis.readBoolean();
}
/**
* Read bytes. Watch out, read may return fewer bytes than requested.
*
* @param ba
* where the bytes go.
* @param off
* offset in buffer, not offset in file.
* @param len
* count of bytes to read.
*
* @return how many bytes read.
* @throws IOException
* if read fails.
*/
public final int read(byte ba[], int off, int len) throws IOException {
// For efficiency, we avoid one layer of wrapper
return is.read(ba, off, len);
}
/**
* read byte.
*
* @return the byte read.
* @throws IOException if read fails.
* @see java.io.DataInput#readByte()
*/
public final byte readByte() throws IOException
{
return dis.readByte();
}
/**
* read only a one-byte boolean.
*
* @return true or false.
* @throws IOException
* if read fails.
* @see java.io.DataInput#readBoolean()
*/
@Override
public final boolean readBoolean() throws IOException {
return dis.readBoolean();
}
/**
* Read on char. like DataInputStream.readChar except little endian.
*
* @return little endian 16-bit unicode char from the stream.
* @throws IOException if read fails.
*/
public final char readChar() throws IOException
{
dis.readFully( work, 0, 2 );
return ( char ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) );
}
/**
* read byte.
*
* @return the byte read.
* @throws IOException
* if read fails.
* @see java.io.DataInput#readByte()
*/
@Override
public final byte readByte() throws IOException {
return dis.readByte();
}
/**
* Read a double. like DataInputStream.readDouble except little endian.
*
* @return little endian IEEE double from the datastream.
* @throws IOException
*/
public final double readDouble() throws IOException
{
return Double.longBitsToDouble( readLong() );
}
/**
* Read on char. like DataInputStream.readChar except little endian.
*
* @return little endian 16-bit unicode char from the stream.
* @throws IOException
* if read fails.
*/
@Override
public final char readChar() throws IOException {
dis.readFully(work, 0, 2);
return (char) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
}
/**
* Read one float. Like DataInputStream.readFloat except little endian.
*
* @return little endian IEEE float from the datastream.
* @throws IOException if read fails.
*/
public final float readFloat() throws IOException
{
return Float.intBitsToFloat( readInt() );
}
/**
* Read a double. like DataInputStream.readDouble except little endian.
*
* @return little endian IEEE double from the datastream.
* @throws IOException
*/
@Override
public final double readDouble() throws IOException {
return Double.longBitsToDouble(readLong());
}
/**
* Read bytes until the array is filled.
*
* @see java.io.DataInput#readFully(byte[])
*/
public final void readFully( byte ba[] ) throws IOException
{
dis.readFully( ba, 0, ba.length );
}
/**
* Read one float. Like DataInputStream.readFloat except little endian.
*
* @return little endian IEEE float from the datastream.
* @throws IOException
* if read fails.
*/
@Override
public final float readFloat() throws IOException {
return Float.intBitsToFloat(readInt());
}
/**
* Read bytes until the count is satisfied.
*
* @throws IOException if read fails.
* @see java.io.DataInput#readFully(byte[],int,int)
*/
public final void readFully( byte ba[],
int off,
int len ) throws IOException
{
dis.readFully( ba, off, len );
}
/**
* Read bytes until the array is filled.
*
* @see java.io.DataInput#readFully(byte[])
*/
@Override
public final void readFully(byte ba[]) throws IOException {
dis.readFully(ba, 0, ba.length);
}
/**
* Read an int, 32-bits. Like DataInputStream.readInt except little endian.
*
* @return little-endian binary int from the datastream
* @throws IOException if read fails.
*/
public final int readInt() throws IOException
{
dis.readFully( work, 0, 4 );
return ( work[ 3 ] ) << 24
| ( work[ 2 ] & 0xff ) << 16
| ( work[ 1 ] & 0xff ) << 8
| ( work[ 0 ] & 0xff );
}
/**
* Read bytes until the count is satisfied.
*
* @throws IOException
* if read fails.
* @see java.io.DataInput#readFully(byte[],int,int)
*/
@Override
public final void readFully(byte ba[], int off, int len) throws IOException {
dis.readFully(ba, off, len);
}
/**
* Read a line.
*
* @return a rough approximation of the 8-bit stream as a 16-bit unicode string
* @throws IOException
* @noinspection deprecation
* @deprecated This method does not properly convert bytes to characters. Use a Reader instead with a little-endian
* encoding.
*/
public final String readLine() throws IOException
{
return dis.readLine();
}
/**
* Read an int, 32-bits. Like DataInputStream.readInt except little endian.
*
* @return little-endian binary int from the datastream
* @throws IOException
* if read fails.
*/
@Override
public final int readInt() throws IOException {
dis.readFully(work, 0, 4);
return (work[3]) << 24 | (work[2] & 0xff) << 16 | (work[1] & 0xff) << 8
| (work[0] & 0xff);
}
/**
* read a long, 64-bits. Like DataInputStream.readLong except little endian.
*
* @return little-endian binary long from the datastream.
* @throws IOException
*/
public final long readLong() throws IOException
{
dis.readFully( work, 0, 8 );
return ( long ) ( work[ 7 ] ) << 56
|
/* long cast needed or shift done modulo 32 */
( long ) ( work[ 6 ] & 0xff ) << 48
| ( long ) ( work[ 5 ] & 0xff ) << 40
| ( long ) ( work[ 4 ] & 0xff ) << 32
| ( long ) ( work[ 3 ] & 0xff ) << 24
| ( long ) ( work[ 2 ] & 0xff ) << 16
| ( long ) ( work[ 1 ] & 0xff ) << 8
| ( long ) ( work[ 0 ] & 0xff );
}
/**
* Read a line.
*
* @return a rough approximation of the 8-bit stream as a 16-bit unicode
* string
* @throws IOException
* @noinspection deprecation
* @deprecated This method does not properly convert bytes to characters.
* Use a Reader instead with a little-endian encoding.
*/
@Deprecated
@Override
public final String readLine() throws IOException {
return dis.readLine();
}
/**
* Read short, 16-bits. Like DataInputStream.readShort except little endian.
*
* @return little endian binary short from stream.
* @throws IOException if read fails.
*/
public final short readShort() throws IOException
{
dis.readFully( work, 0, 2 );
return ( short ) ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) );
}
/**
* read a long, 64-bits. Like DataInputStream.readLong except little endian.
*
* @return little-endian binary long from the datastream.
* @throws IOException
*/
@Override
public final long readLong() throws IOException {
dis.readFully(work, 0, 8);
return (long) (work[7]) << 56 |
/* long cast needed or shift done modulo 32 */
(long) (work[6] & 0xff) << 48 | (long) (work[5] & 0xff) << 40
| (long) (work[4] & 0xff) << 32 | (long) (work[3] & 0xff) << 24
| (long) (work[2] & 0xff) << 16 | (long) (work[1] & 0xff) << 8
| work[0] & 0xff;
}
/**
* Read UTF counted string.
*
* @return String read.
*/
public final String readUTF() throws IOException
{
return dis.readUTF();
}
/**
* Read short, 16-bits. Like DataInputStream.readShort except little endian.
*
* @return little endian binary short from stream.
* @throws IOException
* if read fails.
*/
@Override
public final short readShort() throws IOException {
dis.readFully(work, 0, 2);
return (short) ((work[1] & 0xff) << 8 | (work[0] & 0xff));
}
/**
* Read an unsigned byte. Note: returns an int, even though says Byte (non-Javadoc)
*
* @throws IOException if read fails.
* @see java.io.DataInput#readUnsignedByte()
*/
public final int readUnsignedByte() throws IOException
{
return dis.readUnsignedByte();
}
/**
* Read UTF counted string.
*
* @return String read.
*/
@Override
public final String readUTF() throws IOException {
return dis.readUTF();
}
/**
* Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort except little endian. Note, returns int
* even though it reads a short.
*
* @return little-endian int from the stream.
* @throws IOException if read fails.
*/
public final int readUnsignedShort() throws IOException
{
dis.readFully( work, 0, 2 );
return ( ( work[ 1 ] & 0xff ) << 8 | ( work[ 0 ] & 0xff ) );
}
/**
* Read an unsigned byte. Note: returns an int, even though says Byte
* (non-Javadoc)
*
* @throws IOException
* if read fails.
* @see java.io.DataInput#readUnsignedByte()
*/
@Override
public final int readUnsignedByte() throws IOException {
return dis.readUnsignedByte();
}
/**
* Skip over bytes in the stream. See the general contract of the <code>skipBytes</code> method of
* <code>DataInput</code>.
* <p/>
* Bytes for this operation are read from the contained input stream.
*
* @param n the number of bytes to be skipped.
*
* @return the actual number of bytes skipped.
* @throws IOException if an I/O error occurs.
*/
public final int skipBytes( int n ) throws IOException
{
return dis.skipBytes( n );
}
}
/**
* Read an unsigned short, 16 bits. Like DataInputStream.readUnsignedShort
* except little endian. Note, returns int even though it reads a short.
*
* @return little-endian int from the stream.
* @throws IOException
* if read fails.
*/
@Override
public final int readUnsignedShort() throws IOException {
dis.readFully(work, 0, 2);
return ((work[1] & 0xff) << 8 | (work[0] & 0xff));
}
/**
* Skip over bytes in the stream. See the general contract of the
* <code>skipBytes</code> method of <code>DataInput</code>.
* <p/>
* Bytes for this operation are read from the contained input stream.
*
* @param n
* the number of bytes to be skipped.
*
* @return the actual number of bytes skipped.
* @throws IOException
* if an I/O error occurs.
*/
@Override
public final int skipBytes(int n) throws IOException {
return dis.skipBytes(n);
}
}

View File

@ -26,175 +26,173 @@ import org.junit.*;
import static org.junit.Assert.*;
import org.xml.sax.SAXException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class BuildAndDecodeTest {
@BeforeClass
public static void beforeClass() throws Exception, BrutException {
sTmpDir = new ExtFile(OS.createTempDirectory());
sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig");
sTestNewDir = new ExtFile(sTmpDir, "testapp-new");
LOGGER.info("Unpacking testapp...");
TestUtils.copyResourceDir(BuildAndDecodeTest.class,
"brut/apktool/testapp/", sTestOrigDir);
}
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
@Test
public void isAaptInstalledTest() throws Exception {
assertEquals(true, isAaptPresent());
}
@Test
public void encodeAndDecodeTest() throws BrutException, IOException {
@BeforeClass
public static void beforeClass() throws Exception, BrutException {
sTmpDir = new ExtFile(OS.createTempDirectory());
sTestOrigDir = new ExtFile(sTmpDir, "testapp-orig");
sTestNewDir = new ExtFile(sTmpDir, "testapp-new");
LOGGER.info("Unpacking testapp...");
TestUtils.copyResourceDir(BuildAndDecodeTest.class,
"brut/apktool/testapp/", sTestOrigDir);
}
LOGGER.info("Building testapp.apk...");
File testApk = new File(sTmpDir, "testapp.apk");
ExtFile blank = null;
new Androlib().build(sTestOrigDir, testApk, BuildAndDecodeTest.returnStock(),blank,"");
@AfterClass
public static void afterClass() throws BrutException {
OS.rmdir(sTmpDir);
}
LOGGER.info("Decoding testapp.apk...");
ApkDecoder apkDecoder = new ApkDecoder(testApk);
apkDecoder.setOutDir(sTestNewDir);
apkDecoder.decode();
}
@Test
public void isAaptInstalledTest() throws Exception {
assertEquals(true, isAaptPresent());
}
@Test
public void encodeAndDecodeTest() throws BrutException, IOException {
@Test
public void valuesArraysTest() throws BrutException {
compareValuesFiles("values-mcc001/arrays.xml");
compareValuesFiles("values-mcc002/arrays.xml");
}
LOGGER.info("Building testapp.apk...");
File testApk = new File(sTmpDir, "testapp.apk");
ExtFile blank = null;
new Androlib().build(sTestOrigDir, testApk,
BuildAndDecodeTest.returnStock(), blank, "");
@Test
public void valuesBoolsTest() throws BrutException {
compareValuesFiles("values-mcc001/bools.xml");
}
LOGGER.info("Decoding testapp.apk...");
ApkDecoder apkDecoder = new ApkDecoder(testApk);
apkDecoder.setOutDir(sTestNewDir);
apkDecoder.decode();
}
@Test
public void valuesColorsTest() throws BrutException {
compareValuesFiles("values-mcc001/colors.xml");
}
@Test
public void valuesArraysTest() throws BrutException {
compareValuesFiles("values-mcc001/arrays.xml");
compareValuesFiles("values-mcc002/arrays.xml");
}
@Test
public void valuesDimensTest() throws BrutException {
compareValuesFiles("values-mcc001/dimens.xml");
}
@Test
public void valuesBoolsTest() throws BrutException {
compareValuesFiles("values-mcc001/bools.xml");
}
@Test
public void valuesIdsTest() throws BrutException {
compareValuesFiles("values-mcc001/ids.xml");
}
@Test
public void valuesColorsTest() throws BrutException {
compareValuesFiles("values-mcc001/colors.xml");
}
@Test
public void valuesIntegersTest() throws BrutException {
compareValuesFiles("values-mcc001/integers.xml");
}
@Test
public void valuesDimensTest() throws BrutException {
compareValuesFiles("values-mcc001/dimens.xml");
}
@Test
public void valuesStringsTest() throws BrutException {
compareValuesFiles("values-mcc001/strings.xml");
}
@Test
public void valuesIdsTest() throws BrutException {
compareValuesFiles("values-mcc001/ids.xml");
}
@Test
public void valuesReferencesTest() throws BrutException {
compareValuesFiles("values-mcc002/strings.xml");
}
@Test
public void valuesIntegersTest() throws BrutException {
compareValuesFiles("values-mcc001/integers.xml");
}
@Test
public void crossTypeTest() throws BrutException {
compareValuesFiles("values-mcc003/strings.xml");
compareValuesFiles("values-mcc003/integers.xml");
compareValuesFiles("values-mcc003/bools.xml");
}
@Test
public void valuesStringsTest() throws BrutException {
compareValuesFiles("values-mcc001/strings.xml");
}
@Test
public void xmlLiteralsTest() throws BrutException {
compareXmlFiles("res/xml/literals.xml");
}
@Test
public void valuesReferencesTest() throws BrutException {
compareValuesFiles("values-mcc002/strings.xml");
}
@Test
public void xmlReferencesTest() throws BrutException {
compareXmlFiles("res/xml/references.xml");
}
@Test
public void qualifiersTest() throws BrutException {
compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp" +
"-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key" +
"-navhidden-dpad/strings.xml");
}
private static boolean isAaptPresent() throws Exception {
boolean result = true;
try
{
Process proc = Runtime.getRuntime().exec("aapt");
BufferedReader br = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
String line = null;
while ( (line = br.readLine()) != null){}
} catch (Exception ex){
result = false;
}
return result;
}
@Test
public void crossTypeTest() throws BrutException {
compareValuesFiles("values-mcc003/strings.xml");
compareValuesFiles("values-mcc003/integers.xml");
compareValuesFiles("values-mcc003/bools.xml");
}
private void compareValuesFiles(String path) throws BrutException {
compareXmlFiles("res/" + path,
new ElementNameAndAttributeQualifier("name"));
}
@Test
public void xmlLiteralsTest() throws BrutException {
compareXmlFiles("res/xml/literals.xml");
}
private void compareXmlFiles(String path) throws BrutException {
compareXmlFiles(path, null);
}
@Test
public void xmlReferencesTest() throws BrutException {
compareXmlFiles("res/xml/references.xml");
}
private void compareXmlFiles(String path,
ElementQualifier qualifier) throws BrutException {
DetailedDiff diff;
try {
Reader control = new FileReader(
new File(sTestOrigDir, path));
Reader test = new FileReader(new File(sTestNewDir, path));
@Test
public void qualifiersTest() throws BrutException {
compareValuesFiles("values-mcc004-mnc4-en-rUS-ldrtl-sw100dp-w200dp-h300dp"
+ "-xlarge-long-land-desk-night-xhdpi-finger-keyssoft-12key"
+ "-navhidden-dpad/strings.xml");
}
diff = new DetailedDiff(new Diff(control, test));
} catch (SAXException ex) {
throw new BrutException(ex);
} catch (IOException ex) {
throw new BrutException(ex);
}
private static boolean isAaptPresent() throws Exception {
boolean result = true;
try {
Process proc = Runtime.getRuntime().exec("aapt");
BufferedReader br = new BufferedReader(new InputStreamReader(
proc.getErrorStream()));
String line = null;
while ((line = br.readLine()) != null) {
}
} catch (Exception ex) {
result = false;
}
return result;
}
if (qualifier != null) {
diff.overrideElementQualifier(qualifier);
}
private void compareValuesFiles(String path) throws BrutException {
compareXmlFiles("res/" + path, new ElementNameAndAttributeQualifier(
"name"));
}
assertTrue(path + ": " +
diff.getAllDifferences().toString(), diff.similar());
}
private static HashMap<String, Boolean> returnStock() throws BrutException {
HashMap<String, Boolean> tmp = new HashMap<String, Boolean>();
tmp.put("forceBuildAll", false);
tmp.put("debug", false);
tmp.put("verbose", false);
tmp.put("injectOriginal", false);
tmp.put("framework", false);
tmp.put("update", false);
return tmp;
}
private void compareXmlFiles(String path) throws BrutException {
compareXmlFiles(path, null);
}
private static ExtFile sTmpDir;
private static ExtFile sTestOrigDir;
private static ExtFile sTestNewDir;
private final static Logger LOGGER =
Logger.getLogger(BuildAndDecodeTest.class.getName());
private void compareXmlFiles(String path, ElementQualifier qualifier)
throws BrutException {
DetailedDiff diff;
try {
Reader control = new FileReader(new File(sTestOrigDir, path));
Reader test = new FileReader(new File(sTestNewDir, path));
diff = new DetailedDiff(new Diff(control, test));
} catch (SAXException ex) {
throw new BrutException(ex);
} catch (IOException ex) {
throw new BrutException(ex);
}
if (qualifier != null) {
diff.overrideElementQualifier(qualifier);
}
assertTrue(path + ": " + diff.getAllDifferences().toString(),
diff.similar());
}
private static HashMap<String, Boolean> returnStock() throws BrutException {
HashMap<String, Boolean> tmp = new HashMap<String, Boolean>();
tmp.put("forceBuildAll", false);
tmp.put("debug", false);
tmp.put("verbose", false);
tmp.put("injectOriginal", false);
tmp.put("framework", false);
tmp.put("update", false);
return tmp;
}
private static ExtFile sTmpDir;
private static ExtFile sTestOrigDir;
private static ExtFile sTestNewDir;
private final static Logger LOGGER = Logger
.getLogger(BuildAndDecodeTest.class.getName());
}

View File

@ -1,4 +1,4 @@
/**
/**
* Copyright 2011 Ryszard Wiśniewski <brut.alll@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -32,107 +32,110 @@ import org.xmlpull.v1.*;
*/
public abstract class TestUtils {
public static Map<String, String> parseStringsXml(File file)
throws BrutException {
try {
XmlPullParser xpp = XmlPullParserFactory.newInstance().newPullParser();
xpp.setInput(new FileReader(file));
public static Map<String, String> parseStringsXml(File file)
throws BrutException {
try {
XmlPullParser xpp = XmlPullParserFactory.newInstance()
.newPullParser();
xpp.setInput(new FileReader(file));
int eventType;
String key = null;
Map<String, String> map = new HashMap<String, String>();
while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
if ("string".equals(xpp.getName())) {
int attrCount = xpp.getAttributeCount();
for (int i = 0; i < attrCount; i++) {
if ("name".equals(xpp.getAttributeName(i))) {
key = xpp.getAttributeValue(i);
break;
}
}
}
break;
case XmlPullParser.END_TAG:
if ("string".equals(xpp.getName())) {
key = null;
}
break;
case XmlPullParser.TEXT:
if (key != null) {
map.put(key, xpp.getText());
}
break;
}
}
int eventType;
String key = null;
Map<String, String> map = new HashMap<String, String>();
while ((eventType = xpp.next()) != XmlPullParser.END_DOCUMENT) {
switch (eventType) {
case XmlPullParser.START_TAG:
if ("string".equals(xpp.getName())) {
int attrCount = xpp.getAttributeCount();
for (int i = 0; i < attrCount; i++) {
if ("name".equals(xpp.getAttributeName(i))) {
key = xpp.getAttributeValue(i);
break;
}
}
}
break;
case XmlPullParser.END_TAG:
if ("string".equals(xpp.getName())) {
key = null;
}
break;
case XmlPullParser.TEXT:
if (key != null) {
map.put(key, xpp.getText());
}
break;
}
}
return map;
} catch (IOException ex) {
throw new BrutException(ex);
} catch (XmlPullParserException ex) {
throw new BrutException(ex);
}
}
return map;
} catch (IOException ex) {
throw new BrutException(ex);
} catch (XmlPullParserException ex) {
throw new BrutException(ex);
}
}
/* TODO: move to brut.util.Jar - it's not possible for now, because below
* implementation uses brut.dir. I think I should merge all my projects to
* single brut.common .
*/
public static void copyResourceDir(Class class_, String dirPath, File out)
throws BrutException {
if (! out.exists()) {
out.mkdirs();
}
copyResourceDir(class_, dirPath, new FileDirectory(out));
}
/*
* TODO: move to brut.util.Jar - it's not possible for now, because below
* implementation uses brut.dir. I think I should merge all my projects to
* single brut.common .
*/
public static void copyResourceDir(Class class_, String dirPath, File out)
throws BrutException {
if (!out.exists()) {
out.mkdirs();
}
copyResourceDir(class_, dirPath, new FileDirectory(out));
}
public static void copyResourceDir(Class class_, String dirPath,
Directory out) throws BrutException {
if (class_ == null) {
class_ = Class.class;
}
public static void copyResourceDir(Class class_, String dirPath,
Directory out) throws BrutException {
if (class_ == null) {
class_ = Class.class;
}
URL dirURL = class_.getClassLoader().getResource(dirPath);
if (dirURL != null && dirURL.getProtocol().equals("file")) {
DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out);
return;
}
URL dirURL = class_.getClassLoader().getResource(dirPath);
if (dirURL != null && dirURL.getProtocol().equals("file")) {
DirUtil.copyToDir(new FileDirectory(dirURL.getFile()), out);
return;
}
if (dirURL == null) {
String className = class_.getName().replace(".", "/") + ".class";
dirURL = class_.getClassLoader().getResource(className);
}
if (dirURL == null) {
String className = class_.getName().replace(".", "/") + ".class";
dirURL = class_.getClassLoader().getResource(className);
}
if (dirURL.getProtocol().equals("jar")) {
String jarPath;
try {
jarPath = URLDecoder.decode(
dirURL.getPath().substring(5,
dirURL.getPath().indexOf("!")), "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new BrutException(ex);
}
DirUtil.copyToDir(new FileDirectory(jarPath), out);
}
}
if (dirURL.getProtocol().equals("jar")) {
String jarPath;
try {
jarPath = URLDecoder.decode(dirURL.getPath().substring(
5, dirURL.getPath().indexOf("!")), "UTF-8");
} catch (UnsupportedEncodingException ex) {
throw new BrutException(ex);
}
DirUtil.copyToDir(new FileDirectory(jarPath), out);
}
}
public static class ResValueElementQualifier implements ElementQualifier {
@Override
public boolean qualifyForComparison(Element control, Element test) {
String controlType = control.getTagName();
if ("item".equals(controlType)) {
controlType = control.getAttribute("type");
}
public static class ResValueElementQualifier implements ElementQualifier {
String testType = test.getTagName();
if ("item".equals(testType)) {
testType = test.getAttribute("type");
}
public boolean qualifyForComparison(Element control, Element test) {
String controlType = control.getTagName();
if ("item".equals(controlType)) {
controlType = control.getAttribute("type");
}
String testType = test.getTagName();
if ("item".equals(testType)) {
testType = test.getAttribute("type");
}
return controlType.equals(testType) && control.getAttribute("name")
.equals(test.getAttribute("name"));
}
}
return controlType.equals(testType)
&& control.getAttribute("name").equals(
test.getAttribute("name"));
}
}
}