mirror of
https://github.com/revanced/Apktool.git
synced 2024-12-05 02:22:55 +01:00
Code cleanup of 2013
Signed-off-by: Connor Tumbleson <connor.tumbleson@gmail.com>
This commit is contained in:
parent
f504ceca43
commit
e82c0754de
@ -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 {
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
||||
};
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
@ -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() {
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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>();
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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" };
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
||||
}
|
||||
|
@ -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";
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -25,178 +25,176 @@ import java.util.List;
|
||||
*/
|
||||
public final class ResXmlEncoders {
|
||||
|
||||
public static String escapeXmlChars(String str) {
|
||||
return str.replace("&", "&").replace("<", "<");
|
||||
}
|
||||
public static String escapeXmlChars(String str) {
|
||||
return str.replace("&", "&").replace("<", "<");
|
||||
}
|
||||
|
||||
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(""");
|
||||
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(""");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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+)=\\(([^)]+)\\);");
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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());
|
||||
}
|
||||
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user