Added current version.

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

View File

@ -0,0 +1,152 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResTable;
import brut.common.BrutException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import brut.directory.Util;
import brut.directory.ZipRODirectory;
import brut.util.OS;
import java.io.File;
import java.io.IOException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class Androlib {
private final String mAndroidJar;
private final AndrolibResources mAndRes;
private final AndrolibSmali mSmali;
public Androlib(String androidJar) {
mAndroidJar = androidJar;
mAndRes = new AndrolibResources(mAndroidJar);
mSmali = new AndrolibSmali();
}
public void decode(String apkFileName, String outDirName)
throws AndrolibException {
decode(new File(apkFileName), new File(outDirName));
}
public void decode(File apkFile, File outDir)
throws AndrolibException {
try {
OS.rmdir(outDir);
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
outDir.mkdirs();
ResTable resTable = mAndRes.getResTable(apkFile);
mAndRes.decode(resTable, apkFile, outDir);
File smaliDir = new File(outDir.getPath() + "/smali");
mSmali.baksmali(apkFile, smaliDir);
mAndRes.tagSmaliResIDs(resTable, smaliDir);
try {
Directory in = new ZipRODirectory(apkFile);
Directory out = new FileDirectory(outDir);
if (in.containsDir("assets")) {
Util.copyFiles(in.getDir("assets"), out.createDir("assets"));
}
if (in.containsDir("lib")) {
Util.copyFiles(in.getDir("lib"), out.createDir("lib"));
}
} catch (DirectoryException ex) {
throw new AndrolibException("Could not decode apk", ex);
}
}
public void buildAll() throws AndrolibException {
clean();
new File("build/apk").mkdirs();
buildCopyRawFiles();
buildResources();
buildClasses();
buildPackage();
}
public void clean() throws AndrolibException {
try {
OS.rmdir("build");
OS.rmdir("dist");
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}
public void buildCopyRawFiles() throws AndrolibException {
try {
if (new File("assets").exists()) {
OS.cpdir("assets", "build/apk/assets");
}
if (new File("lib").exists()) {
OS.cpdir("lib", "build/apk/lib");
}
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}
public void buildResources() throws AndrolibException {
File apkFile;
try {
apkFile = File.createTempFile("APKTOOL", null);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
apkFile.delete();
mAndRes.aaptPackage(
apkFile,
new File("AndroidManifest.xml"),
new File("res")
);
mAndRes.updateSmaliResIDs(
mAndRes.getResTable(apkFile), new File("smali"));
try {
Util.copyFiles(new ZipRODirectory(apkFile),
new FileDirectory("build/apk"));
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
apkFile.delete();
}
public void buildClasses() throws AndrolibException {
new AndrolibSmali().smali("smali", "build/apk/classes.dex");
}
public void buildPackage() throws AndrolibException {
File distDir = new File("dist");
if (! distDir.exists()) {
distDir.mkdirs();
}
mAndRes.aaptPackage(new File("dist/out.apk"), null, null,
new File("build/apk"), false);
}
}

View File

@ -0,0 +1,40 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib;
import brut.common.BrutException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class AndrolibException extends BrutException {
public AndrolibException() {
}
public AndrolibException(String message) {
super(message);
}
public AndrolibException(String message, Throwable cause) {
super(message, cause);
}
public AndrolibException(Throwable cause) {
super(cause);
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib;
import java.io.File;
import java.io.IOException;
import org.jf.baksmali.baksmali;
import org.jf.dexlib.DexFile;
import org.jf.smali.main;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
class AndrolibSmali {
public void baksmali(File apkFile, File dir) throws AndrolibException {
baksmali(apkFile.getAbsolutePath(), dir.getAbsolutePath());
}
public void baksmali(String apkFile, String dir) throws AndrolibException {
try {
DexFile dexFile = new DexFile(apkFile);
baksmali.disassembleDexFile(dexFile, false, dir, new String[]{}, "", false, true, true, true, false, 0, false);
} catch (IOException ex) {
throw new AndrolibException(ex);
}
}
public void smali(File dir, File dexFile) throws AndrolibException {
smali(dir.getAbsolutePath(), dexFile.getAbsolutePath());
}
public void smali(String dir, String dexFile) throws AndrolibException {
main.main(new String[]{"smali", dir, "-o", dexFile});
}
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResTable;
import brut.common.BrutException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import brut.directory.ZipRODirectory;
import brut.util.OS;
import java.io.File;
import java.io.IOException;
import org.apache.commons.io.IOUtils;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ApkFile {
private final AndrolibResources mAndRes;
private final File mApkFile;
private final AndrolibSmali mSmali;
private ResTable mResTable;
public ApkFile(AndrolibResources andRes, String apkFileName) {
this(andRes, new File(apkFileName));
}
public ApkFile(AndrolibResources andRes, File apkFile) {
mAndRes = andRes;
mApkFile = apkFile;
mSmali = new AndrolibSmali();
}
public void decode(String outDirName) throws AndrolibException {
decode(new File(outDirName));
}
public void decode(File outDir) throws AndrolibException {
if (outDir.exists()) {
throw new AndrolibException("Output directory already exists: " +
outDir.getAbsolutePath());
}
outDir.mkdirs();
File smaliDir = new File(outDir.getPath() + "/smali");
mAndRes.decode(getResTable(), mApkFile, outDir);
mSmali.baksmali(mApkFile, smaliDir);
mAndRes.tagSmaliResIDs(getResTable(), smaliDir);
try {
Directory in = new ZipRODirectory(mApkFile);
Directory out = new FileDirectory(outDir);
if (in.containsDir("assets")) {
Directory in2 = in.getDir("assets");
Directory out2 = out.createDir("assets");
for (String fileName : in2.getFiles(true)) {
IOUtils.copy(in2.getFileInput(fileName),
out2.getFileOutput(fileName));
}
}
if (in.containsDir("lib")) {
Directory in2 = in.getDir("lib");
Directory out2 = out.createDir("lib");
for (String fileName : in2.getFiles(true)) {
IOUtils.copy(in2.getFileInput(fileName),
out2.getFileOutput(fileName));
}
}
} catch (DirectoryException ex) {
throw new AndrolibException("Could not decode apk", ex);
} catch (IOException ex) {
throw new AndrolibException("Could not decode apk", ex);
}
}
public void build(String inDir) throws AndrolibException {
build(new File(inDir));
}
public void build(File inDir) throws AndrolibException {
try {
File smaliDir = new File(inDir.getPath() + "/smali");
mAndRes.aaptPackage(
mApkFile,
new File(inDir.getPath() + "/AndroidManifest.xml"),
new File(inDir.getPath() + "/res")
);
mAndRes.updateSmaliResIDs(getResTable(), smaliDir);
File tmpDir = OS.createTempDirectory();
try {
mSmali.smali(smaliDir,
new File(tmpDir.getPath() + "/classes.dex"));
if (new File(inDir.getPath() + "/assets").exists()) {
OS.cpdir(inDir.getPath() + "/assets",
tmpDir.getPath() + "/assets");
}
if (new File(inDir.getPath() + "/lib").exists()) {
OS.cpdir(inDir.getPath() + "/lib",
tmpDir.getPath() + "/lib");
}
mAndRes.aaptPackage(
mApkFile,
null,
null,
tmpDir,
true
);
} finally {
OS.rmdir(tmpDir);
}
} catch (BrutException ex) {
throw new AndrolibException(
"Could not build apk for dir: " + inDir.getAbsolutePath(), ex);
}
}
public ResTable getResTable() throws AndrolibException {
if (mResTable == null) {
mResTable = mAndRes.getResTable(mApkFile);
}
return mResTable;
}
}

View File

@ -0,0 +1,218 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res;
import brut.androlib.*;
import brut.androlib.res.data.*;
import brut.androlib.res.data.value.ResFileValue;
import brut.androlib.res.data.value.ResXmlSerializable;
import brut.androlib.res.decoder.*;
import brut.androlib.res.jni.JniPackage;
import brut.androlib.res.jni.JniPackageGroup;
import brut.common.BrutException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import brut.directory.ZipRODirectory;
import brut.util.Jar;
import brut.util.OS;
import java.io.File;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Arrays;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
final public class AndrolibResources {
static {
Jar.load("/libAndroid.so");
}
private final File mAndroidJar;
public AndrolibResources(String androidJar) {
this(new File(androidJar));
}
public AndrolibResources(File androidJar) {
this.mAndroidJar = androidJar;
}
public ResTable getResTable(File apkFile) throws AndrolibException {
ResTable resTable = new ResTable();
loadApk(resTable, new File(getClass().getProtectionDomain().getCodeSource().getLocation().getPath()), false);
loadApk(resTable, apkFile, true);
return resTable;
}
public void decode(ResTable resTable, File apkFile, File outDir)
throws AndrolibException {
ResXmlSerializer serial = getResXmlSerializer(resTable);
ResFileDecoder fileDecoder = getResFileDecoder(serial);
serial.setCurrentPackage(
resTable.listMainPackages().iterator().next());
Directory in, out;
try {
in = new ZipRODirectory(apkFile);
out = new FileDirectory(outDir);
fileDecoder.decode(
in, "AndroidManifest.xml", out, "AndroidManifest.xml", "xml");
in = in.getDir("res");
out = out.createDir("res");
} catch (DirectoryException ex) {
throw new AndrolibException(ex);
}
for (ResPackage pkg : resTable.listMainPackages()) {
serial.setCurrentPackage(pkg);
for (ResResource res : pkg.listFiles()) {
ResFileValue fileValue = (ResFileValue) res.getValue();
fileDecoder.decode(in, fileValue.getStrippedPath(),
out, res.getFilePath());
}
for (ResValuesFile valuesFile : pkg.listValuesFiles()) {
generateValuesFile(valuesFile, out, serial);
}
}
}
public void aaptPackage(File apkFile, File manifest, File resDir)
throws AndrolibException {
aaptPackage(apkFile, manifest, resDir, null, false);
}
public void aaptPackage(File apkFile, File manifest, File resDir,
File rawDir, boolean update) throws AndrolibException {
String[] cmd = new String[12];
int i = 0;
cmd[i++] = "aapt";
cmd[i++] = "p";
if (update) {
cmd[i++] = "-u";
}
cmd[i++] = "-F";
cmd[i++] = apkFile.getAbsolutePath();
cmd[i++] = "-I";
cmd[i++] = mAndroidJar.getAbsolutePath();
if (manifest != null) {
cmd[i++] = "-M";
cmd[i++] = manifest.getAbsolutePath();
}
if (resDir != null) {
cmd[i++] = "-S";
cmd[i++] = resDir.getAbsolutePath();
}
if (rawDir != null) {
cmd[i++] = rawDir.getAbsolutePath();
}
try {
OS.exec(Arrays.copyOf(cmd, i));
} catch (BrutException ex) {
throw new AndrolibException(ex);
}
}
public void tagSmaliResIDs(ResTable resTable, File smaliDir)
throws AndrolibException {
new ResSmaliUpdater().tagResIDs(resTable, smaliDir);
}
public void updateSmaliResIDs(ResTable resTable, File smaliDir) throws AndrolibException {
new ResSmaliUpdater().updateResIDs(resTable, smaliDir);
}
public ResFileDecoder getResFileDecoder(ResXmlSerializer serializer) {
ResStreamDecoderContainer decoders =
new ResStreamDecoderContainer();
decoders.setDecoder("raw", new ResRawStreamDecoder());
decoders.setDecoder("xml",
new ResXmlStreamDecoder(serializer));
return new ResFileDecoder(decoders);
}
public ResXmlSerializer getResXmlSerializer(ResTable resTable) {
ResXmlSerializer serial = new ResXmlSerializer();
serial.setProperty(serial.PROPERTY_SERIALIZER_INDENTATION, " ");
return serial;
}
private void generateValuesFile(ResValuesFile valuesFile, Directory out,
ResXmlSerializer serial) throws AndrolibException {
try {
OutputStream outStream = out.getFileOutput(valuesFile.getPath());
serial.setOutput((outStream), null);
serial.setDecodingEnabled(false);
serial.startDocument(null, null);
serial.startTag(null, "resources");
for (ResResource res : valuesFile.listResources()) {
((ResXmlSerializable) res.getValue())
.serializeToXml(serial, res);
}
serial.endTag(null, "resources");
serial.endDocument();
serial.flush();
outStream.close();
} catch (IOException ex) {
throw new AndrolibException(
"Could not generate: " + valuesFile.getPath(), ex);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not generate: " + valuesFile.getPath(), ex);
}
}
private void loadApk(ResTable resTable, File apkFile, boolean main)
throws AndrolibException {
JniPackageGroup[] groups =
nativeGetPackageGroups(apkFile.getAbsolutePath());
if (groups.length != 1) {
throw new AndrolibException(
"Apk's with multiple or zero package groups not supported");
}
for (int i = 0; i < groups.length; i++) {
// if (groups.length != 1 && i == 0) {
// continue;
// }
for (JniPackage jniPkg : groups[i].packages) {
ResPackage pkg = new JniPackageDecoder().decode(jniPkg, resTable);
resTable.addPackage(pkg, main);
}
}
}
public static String escapeForResXml(String value) {
value = value.replace("'", "\\'");
value = value.replace("\n", "\\n\n");
char c = value.charAt(0);
if (c == '@' || c == '#' || c == '?') {
return '\\' + value;
}
return value;
}
private static final native JniPackageGroup[] nativeGetPackageGroups(
String apkFileName);
}

View File

@ -0,0 +1,146 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResSpec;
import brut.androlib.res.data.ResTable;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import brut.directory.FileDirectory;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Iterator;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.IOUtils;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResSmaliUpdater {
public void tagResIDs(ResTable resTable, File smaliDir)
throws AndrolibException {
Directory dir = null;
try {
dir = new FileDirectory(smaliDir);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not tag res IDs", ex);
}
for (String fileName : dir.getFiles(true)) {
try {
Iterator<String> it =
IOUtils.readLines(dir.getFileInput(fileName)).iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) {
String line = it.next();
if (RES_NAME_PATTERN.matcher(line).matches()) {
out.println(line);
out.println(it.next());
continue;
}
Matcher m = RES_ID_PATTERN.matcher(line);
if (m.matches()) {
int resID = parseResID(m.group(3));
if (resID != -1) {
ResResSpec spec = resTable.getResSpec(resID);
out.println(String.format(
RES_NAME_FORMAT, spec.getFullName()));
}
}
out.println(line);
}
out.close();
} catch (IOException ex) {
throw new AndrolibException(
"Could not tag resIDs for file: " + fileName, ex);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not tag resIDs for file: " + fileName, ex);
} catch (AndrolibException ex) {
throw new AndrolibException(
"Could not tag resIDs for file: " + fileName, ex);
}
}
}
public void updateResIDs(ResTable resTable, File smaliDir)
throws AndrolibException {
try {
Directory dir = new FileDirectory(smaliDir);
for (String fileName : dir.getFiles(true)) {
Iterator<String> it =
IOUtils.readLines(dir.getFileInput(fileName)).iterator();
PrintWriter out = new PrintWriter(dir.getFileOutput(fileName));
while (it.hasNext()) {
String line = it.next();
out.println(line);
Matcher m1 = RES_NAME_PATTERN.matcher(line);
if (! m1.matches()) {
continue;
}
Matcher m2 = RES_ID_PATTERN.matcher(it.next());
if (! m2.matches()) {
throw new AndrolibException();
}
int resID = resTable.getPackage(m1.group(1))
.getType(m1.group(2)).getResSpec(m1.group(3))
.getId().id;
if (m2.group(1) != null) {
out.println(String.format(
RES_ID_FORMAT_FIELD, m2.group(1), resID));
} else {
out.println(String.format(
RES_ID_FORMAT_CONST, m2.group(2), resID));
}
}
out.close();
}
} catch (IOException ex) {
throw new AndrolibException(
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex);
} catch (DirectoryException ex) {
throw new AndrolibException(
"Could not tag res IDs for: " + smaliDir.getAbsolutePath(), ex);
}
}
private int parseResID(String resIDHex) {
if (resIDHex.endsWith("ff")) {
return -1;
}
int resID = Integer.valueOf(resIDHex, 16);
if (resIDHex.length() == 4) {
resID = resID << 16;
}
return resID;
}
private final static String RES_ID_FORMAT_FIELD =
".field %s:I = 0x%08x";
private final static String RES_ID_FORMAT_CONST =
" const %s, 0x%08x";
private final static Pattern RES_ID_PATTERN = Pattern.compile(
"^(?:\\.field (.+?):I =| const(?:|/(?:|high)16) ([pv]\\d+?),) 0x(7[a-f]0[1-9a-f](?:|[0-9a-f]{4}))$");
private final static String RES_NAME_FORMAT =
"# APKTOOL/RES_NAME: %s";
private final static Pattern RES_NAME_PATTERN = Pattern.compile(
"^# APKTOOL/RES_NAME: ([a-zA-Z0-9.]+):([a-z]+)/([a-zA-Z0-9._]+)$");
}

View File

@ -0,0 +1,69 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import java.util.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResConfig {
private final ResConfigFlags mFlags;
private final Map<ResResSpec, ResResource> mResources =
new LinkedHashMap<ResResSpec, ResResource>();
public ResConfig(ResConfigFlags flags) {
this.mFlags = flags;
}
public Set<ResResource> listResources() {
return new LinkedHashSet<ResResource>(mResources.values());
}
public ResResource getResource(ResResSpec spec) throws AndrolibException {
ResResource res = mResources.get(spec);
if (res == null) {
throw new AndrolibException(String.format(
"Undefined resource: spec=%s, config=%s", spec, this));
}
return res;
}
public Set<ResResSpec> listResSpecs() {
return mResources.keySet();
}
public ResConfigFlags getFlags() {
return mFlags;
}
public void addResource(ResResource res)
throws AndrolibException {
ResResSpec spec = res.getResSpec();
if (mResources.put(spec, res) != null) {
throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", spec, this));
}
}
@Override
public String toString() {
return mFlags.toString();
}
}

View File

@ -0,0 +1,342 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.jni.JniConfig;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResConfigFlags {
public final int mcc;
public final int mnc;
public final char[] language;
public final char[] country;
// public final Orientation orientation;
// public final Touchscreen touchscreen;
public final int orientation;
public final int touchscreen;
public final int density;
// public final Keyboard keyboard;
// public final Navigation navigation;
// public final Keys keys;
// public final Nav nav;
public final int keyboard;
public final int navigation;
public final int inputFlags;
public final int screenWidth;
public final int screenHeight;
// public final ScreenSize screenSize;
// public final ScreenLong screenLong;
public final int screenLayout;
public final int sdkVersion;
private final String mQualifiers;
public ResConfigFlags() {
mcc = 0;
mnc = 0;
language = new char[]{'\00', '\00'};
country = new char[]{'\00', '\00'};
orientation = ORIENTATION_ANY;
touchscreen = TOUCHSCREEN_ANY;
density = DENSITY_DEFAULT;
keyboard = KEYBOARD_ANY;
navigation = NAVIGATION_ANY;
inputFlags = KEYSHIDDEN_ANY | NAVHIDDEN_ANY;
screenWidth = 0;
screenHeight = 0;
screenLayout = SCREENLONG_ANY | SCREENSIZE_ANY;
sdkVersion = 0;
mQualifiers = "";
}
public ResConfigFlags(JniConfig cfg) throws AndrolibException {
// if (cfg.mcc == 0 && mnc != 0) {
// throw new AndrolibException(String.format(
// "Invalid IMSI: mcc=%3d, mnc=%3d", mcc, mnc));
// }
mcc = cfg.mcc;
mnc = cfg.mnc;
// if (cfg.language.length == 0 && cfg.country.length != 0) {
// throw new AndrolibException(String.format(
// "Invalid local: language=%s, country=%s",
// cfg.language, cfg.country));
// }
language = cfg.language;
country = cfg.country;
//
// switch (cfg.orientation) {
// case ORIENTATION_ANY:
// orientation = Orientation.ANY;
// break;
// case ORIENTATION_LAND:
// orientation = Orientation.LAND;
// break;
// case ORIENTATION_PORT:
// orientation = Orientation.PORT;
// break;
// case ORIENTATION_SQUARE:
// orientation = Orientation.SQUARE;
// break;
// default:
// throw new AndrolibException(String.format(
// "Invalid orientation: %d", cfg.orientation));
// }
orientation = cfg.orientation;
touchscreen = cfg.touchscreen;
density = cfg.density;
keyboard = cfg.keyboard;
navigation = cfg.navigation;
inputFlags = cfg.inputFlags;
screenWidth = cfg.screenWidth;
screenHeight = cfg.screenHeight;
screenLayout = cfg.screenLayout;
sdkVersion = cfg.sdkVersion;
mQualifiers = generateQualifiers();
}
public String getQualifiers() {
return mQualifiers;
}
private String generateQualifiers() {
StringBuilder ret = new StringBuilder();
if (mcc != 0) {
ret.append("-mcc").append(mcc);
if (mnc != 0) {
ret.append("-mnc").append(mnc);
}
}
if (language[0] != '\00') {
ret.append('-').append(language);
if (country[0] != '\00') {
ret.append("-r").append(country);
}
}
switch (screenLayout & MASK_SCREENSIZE) {
case SCREENSIZE_SMALL:
ret.append("-small");
break;
case SCREENSIZE_NORMAL:
ret.append("-normal");
break;
case SCREENSIZE_LARGE:
ret.append("-large");
break;
}
switch (screenLayout & MASK_SCREENLONG) {
case SCREENLONG_YES:
ret.append("-long");
break;
case SCREENLONG_NO:
ret.append("-notlong");
break;
}
switch (orientation) {
case ORIENTATION_PORT:
ret.append("-port");
break;
case ORIENTATION_LAND:
ret.append("-land");
break;
case ORIENTATION_SQUARE:
ret.append("-square");
break;
}
switch (density) {
case DENSITY_DEFAULT:
break;
case DENSITY_LOW:
ret.append("-ldpi");
break;
case DENSITY_MEDIUM:
ret.append("-mdpi");
break;
case DENSITY_HIGH:
ret.append("-hdpi");
break;
case DENSITY_NONE:
ret.append("-nodpi");
break;
default:
ret.append('-').append(density).append("dpi");
}
switch (touchscreen) {
case TOUCHSCREEN_NOTOUCH:
ret.append("-notouch");
break;
case TOUCHSCREEN_STYLUS:
ret.append("-stylus");
break;
case TOUCHSCREEN_FINGER:
ret.append("-finger");
break;
}
switch (inputFlags & MASK_KEYSHIDDEN) {
case KEYSHIDDEN_NO:
ret.append("-keysexposed");
break;
case KEYSHIDDEN_YES:
ret.append("-keyshidden");
break;
case KEYSHIDDEN_SOFT:
ret.append("-keyssoft");
break;
}
switch (keyboard) {
case KEYBOARD_NOKEYS:
ret.append("-nokeys");
break;
case KEYBOARD_QWERTY:
ret.append("-qwerty");
break;
case KEYBOARD_12KEY:
ret.append("-12key");
break;
}
switch (inputFlags & MASK_NAVHIDDEN) {
case NAVHIDDEN_NO:
ret.append("-navexposed");
break;
case NAVHIDDEN_YES:
ret.append("-navhidden");
break;
}
switch (navigation) {
case NAVIGATION_NONAV:
ret.append("-nonav");
break;
case NAVIGATION_DPAD:
ret.append("-dpad");
break;
case NAVIGATION_TRACKBALL:
ret.append("-trackball");
break;
case NAVIGATION_WHEEL:
ret.append("-wheel");
break;
}
if (screenWidth != 0 && screenHeight != 0) {
if (screenWidth > screenHeight) {
ret.append(String.format("-%dx%d", screenWidth, screenHeight));
} else {
ret.append(String.format("-%dx%d", screenHeight, screenWidth));
}
}
if (sdkVersion != 0) {
ret.append("-v").append(sdkVersion);
}
return ret.toString();
}
@Override
public String toString() {
return ! getQualifiers().equals("") ? getQualifiers() : "[DEFAULT]";
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResConfigFlags other = (ResConfigFlags) obj;
if ((this.mQualifiers == null) ? (other.mQualifiers != null) : !this.mQualifiers.equals(other.mQualifiers)) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 3;
hash = 97 * hash + (this.mQualifiers != null ? this.mQualifiers.hashCode() : 0);
return hash;
}
// public enum Orientation {ANY, PORT, LAND, SQUARE}
// public enum Touchscreen {ANY, NOTOUCH, STYLUS, FINGER}
// public enum Keyboard {ANY, NOKEYS, QWERTY, KEY12}
// public enum Navigation {ANY, NONAV, DPAD, TRACKBALL, WHEEL}
// public enum Keys {ANY, EXPOSED, HIDDEN, SOFT}
// public enum Nav {ANY, EXPOSED, HIDDEN}
// public enum ScreenSize {ANY, SMALL, NORMAL, LARGE}
// public enum ScreenLong {ANY, LONG, NOTLONG}
public final static int ORIENTATION_ANY = 0x0000;
public final static int ORIENTATION_PORT = 0x0001;
public final static int ORIENTATION_LAND = 0x0002;
public final static int ORIENTATION_SQUARE = 0x0003;
public final static int TOUCHSCREEN_ANY = 0x0000;
public final static int TOUCHSCREEN_NOTOUCH = 0x0001;
public final static int TOUCHSCREEN_STYLUS = 0x0002;
public final static int TOUCHSCREEN_FINGER = 0x0003;
public final static int DENSITY_DEFAULT = 0;
public final static int DENSITY_LOW = 120;
public final static int DENSITY_MEDIUM = 160;
public final static int DENSITY_HIGH = 240;
public final static int DENSITY_NONE = 0xffff;
public final static int KEYBOARD_ANY = 0x0000;
public final static int KEYBOARD_NOKEYS = 0x0001;
public final static int KEYBOARD_QWERTY = 0x0002;
public final static int KEYBOARD_12KEY = 0x0003;
public final static int NAVIGATION_ANY = 0x0000;
public final static int NAVIGATION_NONAV = 0x0001;
public final static int NAVIGATION_DPAD = 0x0002;
public final static int NAVIGATION_TRACKBALL = 0x0003;
public final static int NAVIGATION_WHEEL = 0x0004;
public final static int MASK_KEYSHIDDEN = 0x0003;
public final static int KEYSHIDDEN_ANY = 0x0000;
public final static int KEYSHIDDEN_NO = 0x0001;
public final static int KEYSHIDDEN_YES = 0x0002;
public final static int KEYSHIDDEN_SOFT = 0x0003;
public final static int MASK_NAVHIDDEN = 0x000c;
public final static int NAVHIDDEN_ANY = 0x0000;
public final static int NAVHIDDEN_NO = 0x0004;
public final static int NAVHIDDEN_YES = 0x0008;
public final static int MASK_SCREENSIZE = 0x0f;
public final static int SCREENSIZE_ANY = 0x00;
public final static int SCREENSIZE_SMALL = 0x01;
public final static int SCREENSIZE_NORMAL = 0x02;
public final static int SCREENSIZE_LARGE = 0x03;
public final static int MASK_SCREENLONG = 0x30;
public final static int SCREENLONG_ANY = 0x00;
public final static int SCREENLONG_NO = 0x10;
public final static int SCREENLONG_YES = 0x20;
}

View File

@ -0,0 +1,71 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResID {
public final int package_;
public final int type;
public final int entry;
public final int id;
public ResID(int package_, int type, int entry) {
this(package_, type, entry, (package_ << 24) + (type << 16) + entry);
}
public ResID(int id) {
this(id >> 24, (id >> 16) & 0x000000ff, id & 0x0000ffff, id);
}
public ResID(int package_, int type, int entry, int id) {
this.package_ = package_;
this.type = type;
this.entry = entry;
this.id = id;
}
@Override
public String toString() {
return String.format("0x%08x", id);
}
@Override
public int hashCode() {
int hash = 7;
hash = 59 * hash + this.id;
return hash;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResID other = (ResID) obj;
if (this.id != other.id) {
return false;
}
return true;
}
}

View File

@ -0,0 +1,195 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.value.ResFileValue;
import brut.androlib.res.data.value.ResValue;
import brut.androlib.res.data.value.ResValueFactory;
import brut.androlib.res.data.value.ResXmlSerializable;
import brut.util.Duo;
import java.util.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResPackage {
private final ResTable mResTable;
private final int mId;
private final String mName;
private final Map<ResID, ResResSpec> mResSpecs =
new LinkedHashMap<ResID, ResResSpec>();
private final Map<ResConfigFlags, ResConfig> mConfigs =
new LinkedHashMap<ResConfigFlags, ResConfig>();
private final Map<String, ResType> mTypes =
new LinkedHashMap<String, ResType>();
private final Set<ResResource> mFiles = new LinkedHashSet<ResResource>();
private final Map<Duo<ResType, ResConfig>, ResValuesFile> mValuesFiles =
new LinkedHashMap<Duo<ResType, ResConfig>, ResValuesFile>();
private ResValueFactory mValueFactory;
public ResPackage(ResTable resTable, int id, String name) {
this.mResTable = resTable;
this.mId = id;
this.mName = name;
}
public List<ResResSpec> listResSpecs() {
return new ArrayList<ResResSpec>(mResSpecs.values());
}
public boolean hasResSpec(ResID resID) {
return mResSpecs.containsKey(resID);
}
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
ResResSpec spec = mResSpecs.get(resID);
if (spec == null) {
throw new AndrolibException("Undefined resource spec: " + resID);
}
return spec;
}
public List<ResConfig> getConfigs() {
return new ArrayList<ResConfig>(mConfigs.values());
}
public boolean hasConfig(ResConfigFlags flags) {
return mConfigs.containsKey(flags);
}
public ResConfig getConfig(ResConfigFlags flags) throws AndrolibException {
ResConfig config = mConfigs.get(flags);
if (config == null) {
throw new AndrolibException("Undefined config: " + flags);
}
return config;
}
public List<ResType> listTypes() {
return new ArrayList<ResType>(mTypes.values());
}
public boolean hasType(String typeName) {
return mTypes.containsKey(typeName);
}
public ResType getType(String typeName) throws AndrolibException {
ResType type = mTypes.get(typeName);
if (type == null) {
throw new AndrolibException("Undefined type: " + typeName);
}
return type;
}
public Set<ResResource> listFiles() {
return mFiles;
}
public Collection<ResValuesFile> listValuesFiles() {
return mValuesFiles.values();
}
public ResTable getResTable() {
return mResTable;
}
public int getId() {
return mId;
}
public String getName() {
return mName;
}
public void addResSpec(ResResSpec spec) throws AndrolibException {
if (mResSpecs.put(spec.getId(), spec) != null) {
throw new AndrolibException("Multiple resource specs: " + spec);
}
}
public void addConfig(ResConfig config) throws AndrolibException {
if (mConfigs.put(config.getFlags(), config) != null) {
throw new AndrolibException("Multiple configs: " + config);
}
}
public void addType(ResType type) throws AndrolibException {
if (mTypes.put(type.getName(), type) != null) {
throw new AndrolibException("Multiple types: " + type);
}
}
public void addResource(ResResource res) {
ResValue value = res.getValue();
if (value instanceof ResFileValue) {
mFiles.add(res);
}
if (value instanceof ResXmlSerializable) {
ResType type = res.getResSpec().getType();
ResConfig config = res.getConfig();
Duo<ResType, ResConfig> key =
new Duo<ResType, ResConfig>(type, config);
ResValuesFile values = mValuesFiles.get(key);
if (values == null) {
values = new ResValuesFile(type, config);
mValuesFiles.put(key, values);
}
values.addResource(res);
}
}
@Override
public String toString() {
return mName;
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResPackage other = (ResPackage) obj;
if (this.mResTable != other.mResTable && (this.mResTable == null || !this.mResTable.equals(other.mResTable))) {
return false;
}
if (this.mId != other.mId) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 7;
hash = 37 * hash + (this.mResTable != null ? this.mResTable.hashCode() : 0);
hash = 37 * hash + this.mId;
return hash;
}
public ResValueFactory getValueFactory() {
if (mValueFactory == null) {
mValueFactory = new ResValueFactory(this);
}
return mValueFactory;
}
}

View File

@ -0,0 +1,108 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import java.util.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResResSpec {
private final ResID mId;
private final String mName;
private final ResPackage mPackage;
private final ResType mType;
private final Map<ResConfigFlags, ResResource> mResources =
new LinkedHashMap<ResConfigFlags, ResResource>();
public ResResSpec(ResID id, String name, ResPackage pkg, ResType type) {
this.mId = id;
this.mName = name;
this.mPackage = pkg;
this.mType = type;
}
public Set<ResResource> listResources() {
return new LinkedHashSet<ResResource>(mResources.values());
}
public ResResource getResource(ResConfig config) throws AndrolibException {
return getResource(config.getFlags());
}
public ResResource getResource(ResConfigFlags config)
throws AndrolibException {
ResResource res = mResources.get(config);
if (res == null) {
throw new AndrolibException(String.format(
"Undefined resource: spec=%s, config=%s", this, config));
}
return res;
}
public ResResource getDefaultResource() throws AndrolibException {
return getResource(new ResConfigFlags());
}
public String getFullName() {
return getFullName(false, false);
}
public String getFullName(ResPackage relativeToPackage,
boolean excludeType) {
return getFullName(
getPackage().equals(relativeToPackage), excludeType);
}
public String getFullName(boolean excludePackage, boolean excludeType) {
return
(excludePackage ? "" : getPackage().getName() + ":") +
(excludeType ? "" : getType().getName() + "/") + getName();
}
public ResID getId() {
return mId;
}
public String getName() {
return mName;
}
public ResPackage getPackage() {
return mPackage;
}
public ResType getType() {
return mType;
}
public void addResource(ResResource res)
throws AndrolibException {
ResConfigFlags flags = res.getConfig().getFlags();
if (mResources.put(flags, res) != null) {
throw new AndrolibException(String.format(
"Multiple resources: spec=%s, config=%s", this, flags));
}
}
@Override
public String toString() {
return mId.toString() + " " + mType.toString() + "/" + mName;
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.res.data.value.ResValue;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResResource {
private final ResConfig mConfig;
private final ResResSpec mResSpec;
private final ResValue mValue;
public ResResource(ResConfig config, ResResSpec spec,
ResValue value) {
this.mConfig = config;
this.mResSpec = spec;
this.mValue = value;
}
public String getFilePath() {
return mResSpec.getType().getName() +
mConfig.getFlags().getQualifiers() + "/" + mResSpec.getName();
}
public ResConfig getConfig() {
return mConfig;
}
public ResResSpec getResSpec() {
return mResSpec;
}
public ResValue getValue() {
return mValue;
}
@Override
public String toString() {
return getFilePath();
}
}

View File

@ -0,0 +1,97 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.value.ResAttr;
import brut.androlib.res.data.value.ResValue;
import brut.androlib.res.data.value.ResValueFactory;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResTable {
private final Map<Integer, ResPackage> mPackagesById =
new HashMap<Integer, ResPackage>();
private final Map<String, ResPackage> mPackagesByName =
new HashMap<String, ResPackage>();
private final Set<ResPackage> mMainPackages =
new LinkedHashSet<ResPackage>();
public ResTable() {
}
public ResResSpec getResSpec(int resID) throws AndrolibException {
return getResSpec(new ResID(resID));
}
public ResResSpec getResSpec(ResID resID) throws AndrolibException {
return getPackage(resID.package_).getResSpec(resID);
}
public Set<ResPackage> listMainPackages() {
return mMainPackages;
}
public ResPackage getPackage(int id) throws AndrolibException {
ResPackage pkg = mPackagesById.get(id);
if (pkg == null) {
throw new AndrolibException(String.format(
"Undefined package: id=%d", id));
}
return pkg;
}
public ResPackage getPackage(String name) throws AndrolibException {
ResPackage pkg = mPackagesByName.get(name);
if (pkg == null) {
throw new AndrolibException("Undefined package: name=" + name);
}
return pkg;
}
public ResValue getValue(String package_, String type, String name)
throws AndrolibException {
return getPackage(package_).getType(type).getResSpec(name)
.getDefaultResource().getValue();
}
public void addPackage(ResPackage pkg, boolean main)
throws AndrolibException {
Integer id = pkg.getId();
if (mPackagesById.containsKey(id)) {
throw new AndrolibException(
"Multiple packages: id=" + id.toString());
}
String name = pkg.getName();
if (mPackagesByName.containsKey(name)) {
throw new AndrolibException("Multiple packages: name=" + name);
}
mPackagesById.put(id, pkg);
mPackagesByName.put(name, pkg);
if (main) {
mMainPackages.add(pkg);
}
}
}

View File

@ -0,0 +1,294 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import brut.androlib.AndrolibException;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.decoder.ResFileDecoder;
import brut.androlib.res.decoder.ResXmlSerializer;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import java.io.IOException;
import java.util.*;
import java.util.Map.Entry;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public final class ResType {
private final String mName;
// private final TypeConfig mConfig;
private final Map<String, ResResSpec> mResSpecs =
new LinkedHashMap<String, ResResSpec>();
private final ResTable mResTable;
private final ResPackage mPackage;
public ResType(String name, ResTable resTable,
ResPackage package_) {
this.mName = name;
this.mResTable = resTable;
this.mPackage = package_;
}
public String getName() {
return mName;
}
public Set<ResResSpec> listResSpecs() {
return new LinkedHashSet<ResResSpec>(mResSpecs.values());
}
public ResResSpec getResSpec(String name) throws AndrolibException {
ResResSpec spec = mResSpecs.get(name);
if (spec == null) {
throw new AndrolibException(String.format(
"Undefined resource spec: %s/%s", getName(), name));
}
return spec;
}
//
// public void decode(AndrolibResources andRes, ResXmlSerializer serial,
// Directory in, Directory out) throws AndrolibException {
// decodeFiles(andRes, serial, in, out);
// decodeValues(andRes, serial, out);
// }
//
// public void decodeFiles(AndrolibResources andRes, ResXmlSerializer serial,
// Directory in, Directory out) throws AndrolibException {
// if (! mConfig.isFile) {
// return;
// }
//
// ResFileDecoder decoder = andRes.getResFileDecoder(serial);
//
// for (ResResSpec res : listResources()) {
// for (ResResource value : res.listResources()) {
// if (! value.isStrVal()) {
// continue;
// }
// String fileName = value.getStrVal();
// if (! fileName.startsWith("res/")) {
// throw new AndrolibException(
// "Invalid res file location: " + fileName);
// }
// decoder.decode(in, fileName.substring(4), out,
// value.getFilePath());
// }
// }
// }
//
// public void decodeValues(AndrolibResources andRes, ResXmlSerializer serial,
// Directory out) throws AndrolibException {
// if (! mConfig.isValues) {
// return;
// }
//
// boolean oldEscapeRefs = serial.setEscapeRefs(false);
//
// for (Entry<ResConfigFlags, Set<ResResource>> entry :
// groupValuesByConfig().entrySet()) {
// ResConfigFlags config = entry.getKey();
// String filePath = "values" + config.getQualifiers() + "/"
// + mConfig.valuesFileName + ".xml";
//
// try {
// serial.setOutput(out.getFileOutput(filePath), null);
// serial.startDocument(null, null);
// serial.startTag(null, "resources");
//
// for (ResResource value : entry.getValue()) {
// if (mName.equals("array")) {
// decodeArrayValue(serial, value);
// } else {
// serial.startTag(null, mConfig.valuesTagName);
// serial.attribute(
// null, "name", value.getResSpec().getName());
//
// if (mName.equals("style")) {
// decodeStyleValue(serial, value);
// } else if (mName.equals("attr")) {
// decodeAttrValue(serial, value);
// } else {
// decodeSimpleValue(serial, value);
// }
//
// serial.endTag(null, mConfig.valuesTagName);
// }
// }
//
// serial.endTag(null, "resources");
// serial.endDocument();
// } catch (IOException ex) {
// throw new AndrolibException(
// "Could not decode values for:" + filePath, ex);
// } catch (DirectoryException ex) {
// throw new AndrolibException(
// "Could not decode values for:" + filePath, ex);
// }
// }
//
// serial.setEscapeRefs(oldEscapeRefs);
// }
//
// private void decodeSimpleValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
// serial.text(value.toResXMlText());
// }
//
// private void decodeArrayValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
// String arrType = value.getBag().getType();
// String tagName = (arrType != null ? arrType + "-" : "") + "array";
// serial.startTag(null, tagName);
// serial.attribute(
// null, "name", value.getResSpec().getName());
// for (ResResource item : value.getBag().getItems().values()) {
// serial.startTag(null, "item");
// serial.text(item.getStrVal());
// serial.endTag(null, "item");
// }
// serial.endTag(null, tagName);
// }
//
// private void decodeAttrValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
//
// }
//
// private void decodeStyleValue(ResXmlSerializer serial, ResResource value)
// throws IOException, AndrolibException {
// ResBag bag = value.getBag();
// if (bag.getParent().id != 0) {
// serial.attribute(null, "parent", bag.getParentRes()
// .getResReference(false, mPackage.getName()));
//
// for (Entry<ResID, ResResource> entry : bag.getItems().entrySet()) {
// serial.startTag(null, "item");
//
// ResResSpec attr = mResTable.getResSpec(entry.getKey());
// serial.attribute(null, "name", attr.getResReference(false, mPackage.getName(), true).substring(1));
// serial.text(attr.getDefaultResource().decodeAttrValue(entry.getValue().getAsString(), false, mPackage.getName()));
// serial.endTag(null, "item");
// }
// }
// }
public void addResSpec(ResResSpec spec)
throws AndrolibException {
if (mResSpecs.put(spec.getName(), spec) != null) {
throw new AndrolibException(String.format(
"Multiple res specs: %s/%s", getName(), spec.getName()));
}
}
@Override
public String toString() {
return mName;
}
//
// private Map<ResConfigFlags, Set<ResResource>> groupValuesByConfig() {
// Map<ResConfigFlags, Set<ResResource>> grouped =
// new LinkedHashMap<ResConfigFlags, Set<ResResource>>();
//
// for (ResResSpec res : listResSpecs()) {
// for (ResResource value : res.listResources()) {
// ResConfigFlags config = value.getConfig().getFlags();
// Set<ResResource> values = grouped.get(config);
// if (values == null) {
// values = new LinkedHashSet<ResResource>();
// grouped.put(config, values);
// }
// values.add(value);
// }
// }
//
// return grouped;
// }
//
//
// public static ResType factory(String name, ResTable resTable,
// ResPackage package_) throws AndrolibException {
// if (typesConfig == null) {
// loadTypesConfig();
// }
// TypeConfig config = typesConfig.get(name);
// if (config == null) {
// throw new AndrolibException("Invalid type name: " + name);
// }
// return new ResType(name, config, resTable, package_);
// }
//
// private static void loadTypesConfig() {
// typesConfig = new HashMap<String, TypeConfig>();
// typesConfig.put("anim", new FileTypeConfig());
// typesConfig.put("drawable", new FileTypeConfig());
// typesConfig.put("layout", new FileTypeConfig());
// typesConfig.put("menu", new FileTypeConfig());
// typesConfig.put("raw", new FileTypeConfig());
// typesConfig.put("xml", new FileTypeConfig());
//
// typesConfig.put("attr", new ValuesTypeConfig("attrs", "attr"));
// typesConfig.put("dimen", new ValuesTypeConfig("dimens", "dimen"));
// typesConfig.put("string", new ValuesTypeConfig("strings", "string"));
// typesConfig.put("integer", new ValuesTypeConfig("integers", "integer"));
// typesConfig.put("array", new ValuesTypeConfig("arrays", "array"));
// typesConfig.put("style", new ValuesTypeConfig("styles", "style"));
//
// typesConfig.put("color", new TypeConfig(true, true, "colors", "color"));
//
// typesConfig.put("bool", new TypeConfig());
// typesConfig.put("id", new TypeConfig());
//// typesConfig.put("integer", new TypeConfig());
// typesConfig.put("plurals", new TypeConfig());
// }
//
// private static Map<String, TypeConfig> typesConfig;
//
//
// public static class TypeConfig {
// public final boolean isFile;
// public final boolean isValues;
// public final String valuesFileName;
// public final String valuesTagName;
//
// public TypeConfig() {
// this(false, false, null, null);
// }
//
// public TypeConfig(boolean isFile, boolean isValues,
// String valuesFileName, String valuesTagName) {
// this.isFile = isFile;
// this.isValues = isValues;
// this.valuesFileName = valuesFileName;
// this.valuesTagName = valuesTagName;
// }
// }
//
// public static class FileTypeConfig extends TypeConfig {
// public FileTypeConfig() {
// super(true, false, null, null);
// }
// }
//
// public static class ValuesTypeConfig extends TypeConfig {
// public ValuesTypeConfig(String fileName, String tagName) {
// super(false, true, fileName, tagName);
// }
// }
}

View File

@ -0,0 +1,85 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data;
import java.util.LinkedHashSet;
import java.util.Set;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValuesFile {
private final ResType mType;
private final ResConfig mConfig;
private final Set<ResResource> mResources =
new LinkedHashSet<ResResource>();
public ResValuesFile(ResType type, ResConfig config) {
this.mType = type;
this.mConfig = config;
}
public String getPath() {
return "values" + mConfig.getFlags().getQualifiers()
+ "/" + mType.getName()
+ (mType.getName().endsWith("s") ? "" : "s")
+ ".xml";
}
public Set<ResResource> listResources() {
return mResources;
}
public ResType getType() {
return mType;
}
public ResConfig getConfig() {
return mConfig;
}
public void addResource(ResResource res) {
mResources.add(res);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final ResValuesFile other = (ResValuesFile) obj;
if (this.mType != other.mType && (this.mType == null || !this.mType.equals(other.mType))) {
return false;
}
if (this.mConfig != other.mConfig && (this.mConfig == null || !this.mConfig.equals(other.mConfig))) {
return false;
}
return true;
}
@Override
public int hashCode() {
int hash = 5;
hash = 37 * hash + (this.mType != null ? this.mType.hashCode() : 0);
hash = 37 * hash + (this.mConfig != null ? this.mConfig.hashCode() : 0);
return hash;
}
}

View File

@ -0,0 +1,65 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResArrayValue extends ResBagValue implements ResXmlSerializable {
public ResArrayValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
super(parent, items);
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = getType();
type = (type == null ? "" : type + "-") + "array";
serializer.startTag(null, type);
serializer.attribute(null, "name", res.getResSpec().getName());
for (ResScalarValue item : mItems.values()) {
serializer.startTag(null, "item");
serializer.text(item.toResXmlFormat());
serializer.endTag(null, "item");
}
serializer.endTag(null, type);
}
public String getType() {
if (mItems.size() == 0) {
return null;
}
Iterator<ResScalarValue> it = mItems.values().iterator();
String type = it.next().getType();
while (it.hasNext()) {
String itemType = it.next().getType();
if (! type.equals(itemType)) {
return null;
}
}
return type;
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResAttr extends ResBagValue implements ResXmlSerializable {
private final int mType;
public ResAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items);
mType = type;
}
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
return value.toResXmlFormat();
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String type = getTypeAsString();
serializer.startTag(null, "attr");
serializer.attribute(null, "name", res.getResSpec().getName());
if (type != null) {
serializer.attribute(null, "format", type);
}
serializeBody(serializer, res);
serializer.endTag(null, "attr");
}
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {}
protected String getTypeAsString() {
String s = "";
if ((mType & TYPE_REFERENCE) != 0) {
s += "|reference";
}
if ((mType & TYPE_STRING) != 0) {
s += "|string";
}
if ((mType & TYPE_INT) != 0) {
s += "|integer";
}
if ((mType & TYPE_BOOL) != 0) {
s += "|boolean";
}
if ((mType & TYPE_COLOR) != 0) {
s += "|color";
}
if ((mType & TYPE_FLOAT) != 0) {
s += "|float";
}
if ((mType & TYPE_DIMEN) != 0) {
s += "|dimension";
}
if ((mType & TYPE_FRACTION) != 0) {
s += "|fraction";
}
if (s.isEmpty()) {
return null;
}
return s.substring(1);
}
private final static int TYPE_REFERENCE = 0x01;
private final static int TYPE_STRING = 0x02;
private final static int TYPE_INT = 0x04;
private final static int TYPE_BOOL = 0x08;
private final static int TYPE_COLOR = 0x10;
private final static int TYPE_FLOAT = 0x20;
private final static int TYPE_DIMEN = 0x40;
private final static int TYPE_FRACTION = 0x80;
private final static int TYPE_ANY_STRING = 0xee;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResAttrFactory {
static ResValue factory(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
int type = ((ResIntValue) items.values().iterator().next()).getValue();
int attrType = type & 0x0000ffff;
switch (type & 0x00ff0000) {
case TYPE_ENUM:
return new ResEnumAttr(parent, items, attrType);
case TYPE_SET:
return new ResSetAttr(parent, items, attrType);
}
return new ResAttr(parent, items, attrType);
}
private final static int TYPE_ENUM = 0x00010000;
private final static int TYPE_SET = 0x00020000;
}

View File

@ -0,0 +1,42 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResBagValue extends ResValue {
protected final ResReferenceValue mParent;
protected final Map<ResReferenceValue, ResScalarValue> mItems;
public ResBagValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
this.mParent = parent;
this.mItems = items;
}
public ResReferenceValue getParent() {
return mParent;
}
public Map<ResReferenceValue, ResScalarValue> getItems() {
return mItems;
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResBoolValue extends ResScalarValue implements ResXmlSerializable {
private final boolean mValue;
public ResBoolValue(boolean value) {
super("bool");
this.mValue = value;
}
public boolean getValue() {
return mValue;
}
@Override
public String toResXmlFormat() {
return mValue ? "true" : "false";
}
}

View File

@ -0,0 +1,32 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResColorValue extends ResIntValue {
public ResColorValue(int value) {
super(value, "color");
}
@Override
public String toResXmlFormat() {
return String.format("#%08x", mValue);
}
}

View File

@ -0,0 +1,58 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResEnumAttr extends ResMapAttr {
public ResEnumAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items, type);
}
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (value instanceof ResIntValue) {
String ret = getItemsMap().get(((ResIntValue) value).getValue());
if (ret != null) {
return ret;
}
}
return super.convertToResXmlFormat(value);
}
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (Entry<Integer, String> entry : getItemsMap().entrySet()) {
serializer.startTag(null, "enum");
serializer.attribute(null, "name", entry.getValue());
serializer.attribute(null, "value", String.valueOf(entry.getKey()));
serializer.endTag(null, "enum");
}
}
}

View File

@ -0,0 +1,43 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFileValue extends ResValue {
private final String mPath;
public ResFileValue(String path) {
this.mPath = path;
}
public String getPath() {
return mPath;
}
public String getStrippedPath() throws AndrolibException {
if (! mPath.startsWith("res/")) {
throw new AndrolibException(
"File path does not start with \"res/\": " + mPath);
}
return mPath.substring(4);
}
}

View File

@ -0,0 +1,39 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFloatValue extends ResScalarValue {
private final float mValue;
public ResFloatValue(float value) {
super("float");
this.mValue = value;
}
public float getValue() {
return mValue;
}
@Override
public String toResXmlFormat() {
return String.valueOf(mValue);
}
}

View File

@ -0,0 +1,36 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResIdValue extends ResValue {
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
serializer.startTag(null, "item");
serializer.attribute(null, "type", "id");
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.endTag(null, "item");
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResIntValue extends ResScalarValue implements ResXmlSerializable {
protected final int mValue;
public ResIntValue(int value) {
this(value, "integer");
}
public ResIntValue(int value, String type) {
super(type);
this.mValue = value;
}
public int getValue() {
return mValue;
}
@Override
public String toResXmlFormat() throws AndrolibException {
return String.valueOf(mValue);
}
}

View File

@ -0,0 +1,61 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public abstract class ResMapAttr extends ResAttr {
private Map<Integer, String> mMap;
public ResMapAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items, type);
}
protected Map<Integer, String> getItemsMap() throws AndrolibException {
if (mMap == null) {
loadItemsMap();
}
return mMap;
}
private void loadItemsMap() throws AndrolibException {
mMap = new LinkedHashMap<Integer, String>();
Iterator<Entry<ResReferenceValue, ResScalarValue>> it =
mItems.entrySet().iterator();
it.next();
while (it.hasNext()) {
Entry<ResReferenceValue, ResScalarValue> entry = it.next();
// TODO
if (entry.getKey().getValue() < 0x01010000) {
continue;
}
mMap.put(
((ResIntValue) entry.getValue()).getValue(),
entry.getKey().getReferent().getName());
}
}
}

View File

@ -0,0 +1,75 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResPluralsValue extends ResBagValue implements ResXmlSerializable {
public ResPluralsValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
super(parent, items);
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
serializer.startTag(null, "plurals");
serializer.attribute(null, "name", res.getResSpec().getName());
for (Entry<String, String> entry : getPluralsMap().entrySet()) {
serializer.startTag(null, "item");
serializer.attribute(null, "quantity", entry.getKey());
serializer.text(entry.getValue());
serializer.endTag(null, "item");
}
serializer.endTag(null, "plurals");
}
private Map<String, String> getPluralsMap() {
Map<String, String> plurals = new LinkedHashMap<String, String>();
for (Entry<ResReferenceValue, ResScalarValue> entry
: mItems.entrySet()) {
String quantity = getQuantityMap()[
(entry.getKey().getValue() & 0xffff) - 4];
if (quantity != null) {
String value = ((ResStringValue) entry.getValue()).getValue();
plurals.put(quantity, AndrolibResources.escapeForResXml(value));
}
}
return plurals;
}
private static String[] getQuantityMap() {
if (quantityMap == null) {
quantityMap = new String[]
{"other", "zero", "one", "two", "few", "many"};
}
return quantityMap;
}
private static String[] quantityMap;
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResResSpec;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResReferenceValue extends ResIntValue {
private final ResPackage mPackage;
private final boolean mTheme;
public ResReferenceValue(ResPackage package_, int value) {
this(package_, value, false);
}
public ResReferenceValue(ResPackage package_, int value, boolean theme) {
super(value);
mPackage = package_;
mTheme = theme;
}
@Override
public String toResXmlFormat() throws AndrolibException {
if (isNull()) {
return "@null";
}
// try {
return
(mTheme ? '?' : '@') +
getReferent().getFullName(mPackage, mTheme);
// } catch (AndrolibException ex) {
// return "@" + String.valueOf(mValue);
// }
}
public ResResSpec getReferent() throws AndrolibException {
return mPackage.getResTable().getResSpec(getValue());
}
public boolean isNull() {
return mValue == 0;
}
}

View File

@ -0,0 +1,50 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public abstract class ResScalarValue extends ResValue
implements ResXmlPrintable, ResXmlSerializable {
protected final String mType;
protected ResScalarValue(String type) {
mType = type;
}
public abstract String toResXmlFormat() throws AndrolibException;
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
String tagName = res.getResSpec().getType().getName();
serializer.startTag(null, tagName);
serializer.attribute(null, "name", res.getResSpec().getName());
serializer.text(toResXmlFormat());
serializer.endTag(null, tagName);
}
public String getType() {
return mType;
}
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResSetAttr extends ResMapAttr {
public ResSetAttr(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items, int type) {
super(parent, items, type);
}
@Override
public String convertToResXmlFormat(ResScalarValue value)
throws AndrolibException {
if (! (value instanceof ResIntValue)) {
return super.convertToResXmlFormat(value);
}
int intVal = ((ResIntValue) value).getValue();
String strVal = "";
for (Entry<Integer, String> entry : getItemsMap().entrySet()) {
int flag = entry.getKey();
if ((intVal & flag) == flag) {
strVal += "|" + entry.getValue();
}
}
if (strVal.isEmpty()) {
return "";
}
return strVal.substring(1);
}
@Override
protected void serializeBody(XmlSerializer serializer, ResResource res)
throws AndrolibException, IOException {
for (Entry<Integer, String> entry : getItemsMap().entrySet()) {
serializer.startTag(null, "flag");
serializer.attribute(null, "name", entry.getValue());
serializer.attribute(null, "value",
String.format("0x%08x", entry.getKey()));
serializer.endTag(null, "flag");
}
}
}

View File

@ -0,0 +1,45 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.res.AndrolibResources;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResStringValue extends ResScalarValue
implements ResXmlSerializable {
private final String mValue;
public ResStringValue(String value) {
super("string");
this.mValue = value;
}
public String getValue() {
return mValue;
}
@Override
public String toResXmlFormat() {
if (mValue.isEmpty()) {
return "";
}
return AndrolibResources.escapeForResXml(mValue);
}
}

View File

@ -0,0 +1,62 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResSpec;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import java.util.Map;
import java.util.Map.Entry;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResStyleValue extends ResBagValue implements ResXmlSerializable {
public ResStyleValue(ResReferenceValue parent,
Map<ResReferenceValue, ResScalarValue> items) {
super(parent, items);
}
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException {
serializer.startTag(null, "style");
serializer.attribute(null, "name", res.getResSpec().getName());
if (! mParent.isNull()) {
serializer.attribute(null, "parent", mParent.toResXmlFormat());
}
for (Entry<ResReferenceValue, ResScalarValue> entry
: mItems.entrySet()) {
ResResSpec spec = entry.getKey().getReferent();
ResAttr attr = (ResAttr) spec.getDefaultResource().getValue();
String value = attr.convertToResXmlFormat(entry.getValue());
if (value == null) {
continue;
}
serializer.startTag(null, "item");
serializer.attribute(null, "name",
spec.getFullName(res.getResSpec().getPackage(), true));
serializer.text(value);
serializer.endTag(null, "item");
}
serializer.endTag(null, "style");
}
}

View File

@ -0,0 +1,25 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValue {
}

View File

@ -0,0 +1,157 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.ResTable;
import brut.androlib.res.data.ResType;
import brut.androlib.res.jni.JniBagItem;
import brut.androlib.res.jni.JniEntry;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResValueFactory {
private final ResPackage mPackage;
public ResValueFactory(ResPackage pakage_) {
this.mPackage = pakage_;
}
public ResScalarValue factory(String string) {
if (string.isEmpty()) {
return new ResStringValue(string);
}
char c = string.charAt(0);
if (c == '@' || c == '?') {
return newReference(
Integer.parseInt(string.substring(1)), c == '?');
}
if (c == '#') {
return new ResColorValue(
(int) Long.parseLong(string.substring(1), 16));
}
try {
if (string.startsWith("0x")) {
return new ResIntValue(
(int) Long.parseLong(string.substring(2), 16));
}
return new ResIntValue(Integer.parseInt(string));
} catch (NumberFormatException ex) {}
return new ResStringValue(string);
}
public ResValue factory(JniEntry entry)
throws AndrolibException {
if ("id".equals(entry.type)) {
return new ResIdValue();
}
switch (entry.valueType) {
case TYPE_BAG:
return bagFactory(entry);
case TYPE_REFERENCE:
return newReference(entry.intVal);
case TYPE_ATTRIBUTE:
return newReference(entry.intVal, true);
case TYPE_INT_BOOLEAN:
return new ResBoolValue(entry.boolVal);
case TYPE_INT_DEC:
case TYPE_INT_HEX:
return new ResIntValue(entry.intVal);
case TYPE_FLOAT:
return new ResFloatValue(entry.floatVal);
case TYPE_INT_COLOR_ARGB4:
case TYPE_INT_COLOR_ARGB8:
case TYPE_INT_COLOR_RGB4:
case TYPE_INT_COLOR_RGB8:
return new ResColorValue(entry.intVal);
case TYPE_STRING:
if (entry.strVal.startsWith("res/")) {
return new ResFileValue(entry.strVal);
}
case TYPE_DIMENSION:
case TYPE_FRACTION:
return new ResStringValue(entry.strVal);
}
throw new AndrolibException(String.format(
"Unknown value type for %s/%s: ",
entry.type, entry.name, String.valueOf(entry.valueType)));
}
private ResValue bagFactory(JniEntry entry)
throws AndrolibException {
ResReferenceValue parent = newReference(entry.bagParent);
Map<ResReferenceValue, ResScalarValue> items =
convertItems(entry.bagItems);
String type = entry.type;
if ("array".equals(type)) {
return new ResArrayValue(parent, items);
}
if ("style".equals(type)) {
return new ResStyleValue(parent, items);
}
if ("plurals".equals(type)) {
return new ResPluralsValue(parent, items);
}
if ("attr".equals(type)) {
return ResAttrFactory.factory(parent, items);
}
return new ResBagValue(parent, items);
}
private ResReferenceValue newReference(int resID) {
return newReference(resID, false);
}
private ResReferenceValue newReference(int resID, boolean theme) {
return new ResReferenceValue(mPackage, resID, theme);
}
private Map<ResReferenceValue, ResScalarValue> convertItems(
JniBagItem[] jniItems) throws AndrolibException {
Map<ResReferenceValue, ResScalarValue> items =
new LinkedHashMap<ResReferenceValue, ResScalarValue>();
for (int i = 0; i < jniItems.length; i++) {
JniBagItem jniItem = jniItems[i];
items.put(newReference(jniItem.resID),
(ResScalarValue) factory(jniItem.entry));
}
return items;
}
private final static int TYPE_NULL = 0x00;
private final static int TYPE_REFERENCE = 0x01;
private final static int TYPE_ATTRIBUTE = 0x02;
private final static int TYPE_STRING = 0x03;
private final static int TYPE_FLOAT = 0x04;
private final static int TYPE_DIMENSION = 0x05;
private final static int TYPE_FRACTION = 0x06;
private final static int TYPE_INT_DEC = 0x10;
private final static int TYPE_INT_HEX = 0x11;
private final static int TYPE_INT_BOOLEAN = 0x12;
private final static int TYPE_INT_COLOR_ARGB8 = 0x1c;
private final static int TYPE_INT_COLOR_RGB8 = 0x1d;
private final static int TYPE_INT_COLOR_ARGB4 = 0x1e;
private final static int TYPE_INT_COLOR_RGB4 = 0x1f;
private final static int TYPE_BAG = -0x01;
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResXmlPrintable {
public String toResXmlFormat() throws AndrolibException;
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.data.value;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.ResResource;
import java.io.IOException;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResXmlSerializable {
public void serializeToXml(XmlSerializer serializer, ResResource res)
throws IOException, AndrolibException;
}

View File

@ -0,0 +1,83 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import brut.androlib.res.data.*;
import brut.androlib.res.data.value.ResValue;
import brut.androlib.res.data.value.ResValueFactory;
import brut.androlib.res.jni.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniPackageDecoder {
public ResPackage decode(JniPackage jniPkg, ResTable resTable)
throws AndrolibException {
ResPackage pkg = new ResPackage(resTable, jniPkg.id, jniPkg.name);
ResValueFactory valueFactory = pkg.getValueFactory();
JniConfig[] jniConfigs = jniPkg.configs;
for (int i = 0; i < jniConfigs.length; i++) {
JniConfig jniConfig = jniConfigs[i];
ResConfigFlags flags = new ResConfigFlags(jniConfig);
ResConfig config;
if (pkg.hasConfig(flags)) {
config = pkg.getConfig(flags);
} else {
config = new ResConfig(flags);
pkg.addConfig(config);
}
JniEntry[] jniEntries = jniConfig.entries;
for (int j = 0; j < jniEntries.length; j++) {
JniEntry jniEntry = jniEntries[j];
ResType type;
String typeName = jniEntry.type;
if (pkg.hasType(typeName)) {
type = pkg.getType(typeName);
} else {
type = new ResType(typeName, resTable, pkg);
pkg.addType(type);
}
ResID resID = new ResID(jniEntry.resID);
ResResSpec spec;
if (pkg.hasResSpec(resID)) {
spec = pkg.getResSpec(resID);
} else {
spec = new ResResSpec(resID, jniEntry.name, pkg, type);
pkg.addResSpec(spec);
type.addResSpec(spec);
}
ResValue value = valueFactory.factory(jniEntry);
ResResource res =
new ResResource(config, spec, value);
config.addResource(res);
spec.addResource(res);
pkg.addResource(res);
}
}
return pkg;
}
}

View File

@ -0,0 +1,87 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import brut.directory.Directory;
import brut.directory.DirectoryException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResFileDecoder {
private final ResStreamDecoderContainer mDecoders;
public ResFileDecoder(ResStreamDecoderContainer decoders) {
this.mDecoders = decoders;
}
public void decode(Directory inDir, String inFileName, Directory outDir,
String outResName) throws AndrolibException {
String ext = "";
int extPos = inFileName.lastIndexOf(".");
if (extPos != -1) {
ext = inFileName.substring(extPos);
}
if (inFileName.startsWith("raw/")) {
decode(inDir, inFileName, outDir, outResName + ext, "raw");
return;
}
if (inFileName.endsWith(".9.png")) {
decode(inDir, inFileName, outDir, outResName + ".png", "raw");
return;
}
if (inFileName.endsWith(".xml")) {
decode(inDir, inFileName, outDir, outResName + ".xml", "xml");
return;
}
if (inFileName.endsWith(".html")) {
decode(inDir, inFileName, outDir, outResName + ".html", "xml");
return;
}
decode(inDir, inFileName, outDir, outResName + ext, "raw");
}
public void decode(Directory inDir, String inFileName, Directory outDir,
String outFileName, String decoder) throws AndrolibException {
try {
InputStream in = inDir.getFileInput(inFileName);
OutputStream out = outDir.getFileOutput(outFileName);
mDecoders.decode(in, out, decoder);
in.close();
out.close();
} catch (AndrolibException ex) {
throw new AndrolibException(String.format(
"Could not decode file \"%s\" to \"%s\"",
inFileName, outFileName), ex);
} catch (IOException ex) {
throw new AndrolibException(String.format(
"Could not decode file \"%s\" to \"%s\"",
inFileName, outFileName), ex);
} catch (DirectoryException ex) {
throw new AndrolibException(String.format(
"Could not decode file \"%s\" to \"%s\"",
inFileName, outFileName), ex);
}
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.commons.io.IOUtils;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResRawStreamDecoder implements ResStreamDecoder {
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
IOUtils.copy(in, out);
} catch (IOException ex) {
throw new AndrolibException("Could not decode raw stream", ex);
}
}
}

View File

@ -0,0 +1,30 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public interface ResStreamDecoder {
public void decode(InputStream in, OutputStream out)
throws AndrolibException;
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.AndrolibException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResStreamDecoderContainer {
private final Map<String, ResStreamDecoder> mDecoders =
new HashMap<String, ResStreamDecoder>();
public void decode(InputStream in, OutputStream out, String decoderName)
throws AndrolibException {
getDecoder(decoderName).decode(in, out);
}
public ResStreamDecoder getDecoder(String name) throws AndrolibException {
ResStreamDecoder decoder = mDecoders.get(name);
if (decoder== null) {
throw new AndrolibException("Undefined decoder: " + name);
}
return decoder;
}
public void setDecoder(String name, ResStreamDecoder decoder) {
mDecoders.put(name, decoder);
}
}

View File

@ -0,0 +1,109 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.decoder;
import brut.androlib.*;
import brut.androlib.res.AndrolibResources;
import brut.androlib.res.data.ResPackage;
import brut.androlib.res.data.value.ResAttr;
import java.io.IOException;
import org.xmlpull.mxp1_serializer.MXSerializer;
import org.xmlpull.v1.XmlSerializer;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResXmlSerializer extends MXSerializer {
private final static String RES_NAMESPACE =
"http://schemas.android.com/apk/res/android";
private ResPackage mCurrentPackage;
private boolean mDecodingEnabled = true;
@Override
public XmlSerializer attribute(String namespace, String name, String value)
throws IOException, IllegalArgumentException, IllegalStateException
{
if (! mDecodingEnabled) {
return super.attribute(namespace, name, value);
}
if (namespace == null || namespace.isEmpty()) {
return super.attribute(namespace, name,
// AndrolibResources.escapeForResXml(value)
value
);
}
String pkgName = RES_NAMESPACE.equals(namespace) ?
"android" : mCurrentPackage.getName();
try {
ResAttr attr = (ResAttr) mCurrentPackage.getResTable()
.getValue(pkgName, "attr", name);
value = attr.convertToResXmlFormat(
mCurrentPackage.getValueFactory().factory(value));
} catch (AndrolibException ex) {
throw new IllegalArgumentException(String.format(
"could not decode attribute: ns=%s, name=%s, value=%s",
getPrefix(namespace, false), name, value), ex);
}
if (value == null) {
return this;
}
// if ("id".equals(name) && value.startsWith("@id")) {
if (value.startsWith("@id")) {
value = "@+id" + value.substring(3);
}
return super.attribute(namespace, name, value);
}
@Override
public XmlSerializer text(String text) throws IOException {
if (mDecodingEnabled) {
text = AndrolibResources.escapeForResXml(text);
}
return super.text(text);
}
@Override
public XmlSerializer text(char[] buf, int start, int len) throws IOException {
if (mDecodingEnabled) {
return this.text(new String(buf, start, len));
}
return super.text(buf, start, len);
}
@Override
public void startDocument(String encoding, Boolean standalone) throws
IOException, IllegalArgumentException, IllegalStateException {
super.startDocument(encoding != null ? encoding : "UTF-8", standalone);
super.out.write("\n");
super.setPrefix("android", RES_NAMESPACE);
}
public void setCurrentPackage(ResPackage package_) {
mCurrentPackage = package_;
}
public boolean setDecodingEnabled(boolean escapeRefs) {
boolean oldVal = mDecodingEnabled;
this.mDecodingEnabled = escapeRefs;
return oldVal;
}
}

View File

@ -0,0 +1,80 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.decoder;
import android.content.res.XmlBlock;
import brut.androlib.AndrolibException;
import java.io.*;
import org.apache.commons.io.IOUtils;
import org.xmlpull.v1.*;
import org.xmlpull.v1.wrapper.*;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class ResXmlStreamDecoder implements ResStreamDecoder {
private final ResXmlSerializer mSerializer;
public ResXmlStreamDecoder(ResXmlSerializer serializer) {
this.mSerializer = serializer;
}
public void decode(InputStream in, OutputStream out)
throws AndrolibException {
try {
XmlPullParserWrapper par =
getResXmlParserWrapper(in);
XmlSerializerWrapper ser =
getXmlWrapperFactory().newSerializerWrapper(mSerializer);
ser.setOutput(out, null);
mSerializer.setDecodingEnabled(true);
while (par.nextToken() != XmlPullParser.END_DOCUMENT) {
ser.event(par);
}
ser.flush();
} catch (IOException ex) {
throw new AndrolibException("could not decode XML stream", ex);
} catch (IllegalArgumentException ex) {
throw new AndrolibException("could not decode XML stream", ex);
} catch (IllegalStateException ex) {
throw new AndrolibException("could not decode XML stream", ex);
} catch (XmlPullParserException ex) {
throw new AndrolibException("could not decode XML stream", ex);
}
}
private XmlPullParserWrapper getResXmlParserWrapper(InputStream in)
throws IOException, XmlPullParserException {
XmlBlock xml = new XmlBlock(copyStreamToByteArray(in));
XmlPullParser parser = xml.newParser();
return getXmlWrapperFactory().newPullParserWrapper(parser);
}
private XmlPullWrapperFactory getXmlWrapperFactory()
throws XmlPullParserException {
return XmlPullWrapperFactory.newInstance();
}
private byte[] copyStreamToByteArray(InputStream in) throws IOException {
ByteArrayOutputStream out = new ByteArrayOutputStream();
IOUtils.copy(in, out);
return out.toByteArray();
}
}

View File

@ -0,0 +1,31 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniBagItem {
public final int resID;
public final JniEntry entry;
public JniBagItem(int resID, JniEntry entry) {
this.resID = resID;
this.entry = entry;
}
}

View File

@ -0,0 +1,66 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniConfig {
public final int mcc;
public final int mnc;
public final char[] language;
public final char[] country;
public final int orientation;
public final int touchscreen;
public final int density;
public final int keyboard;
public final int navigation;
public final int inputFlags;
public final int screenWidth;
public final int screenHeight;
public final int screenLayout;
public final int sdkVersion;
public final JniEntry[] entries;
public JniConfig(int mcc, int mnc, char[] language, char[] country,
int orientation, int touchscreen, int density, int keyboard,
int navigation, int inputFlags, int screenWidth, int screenHeight,
int screenLayout, int sdkVersion, JniEntry[] entries) {
this.mcc = mcc;
this.mnc = mnc;
this.language = language;
this.country = country;
this.orientation = orientation;
this.touchscreen = touchscreen;
this.density = density;
this.keyboard = keyboard;
this.navigation = navigation;
this.inputFlags = inputFlags;
this.screenWidth = screenWidth;
this.screenHeight = screenHeight;
this.screenLayout = screenLayout;
this.sdkVersion = sdkVersion;
this.entries = entries;
}
}

View File

@ -0,0 +1,52 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniEntry {
public final int resID;
public final String name;
public final String type;
public final int valueType;
public final boolean boolVal;
public final int intVal;
public final float floatVal;
public final String strVal;
public final int bagParent;
public final JniBagItem[] bagItems;
public JniEntry(int resID, String name, String type, int valueType,
boolean boolVal, int intVal, float floatVal, String strVal,
int bagParent, JniBagItem[] bagItems) {
this.resID = resID;
this.name = name;
this.type = type;
this.valueType = valueType;
this.boolVal = boolVal;
this.intVal = intVal;
this.floatVal = floatVal;
this.strVal = strVal;
this.bagParent = bagParent;
this.bagItems = bagItems;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniPackage {
public final int id;
public final String name;
public final JniConfig[] configs;
public JniPackage(int id, String name, JniConfig[] configs) {
this.id = id;
this.name = name;
this.configs = configs;
}
}

View File

@ -0,0 +1,29 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniPackageGroup {
public final JniPackage[] packages;
public JniPackageGroup(JniPackage[] packages) {
this.packages = packages;
}
}

View File

@ -0,0 +1,33 @@
/*
* Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* under the License.
*/
package brut.androlib.res.jni;
/**
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
*/
public class JniType {
public final int entryCount;
public final int typeSpecFlags;
public final JniConfig[] configs;
public JniType(int entryCount, int typeSpecFlags, JniConfig[] configs) {
this.entryCount = entryCount;
this.typeSpecFlags = typeSpecFlags;
this.configs = configs;
}
}