mirror of
https://github.com/revanced/Apktool.git
synced 2024-12-12 05:47:46 +01:00
Merged in "yyj" fixes along with a few of my own. 95% yyj work :)
This commit is contained in:
parent
e9738642b2
commit
282bdae126
@ -211,8 +211,9 @@ public class Main {
|
|||||||
System.out.println(
|
System.out.println(
|
||||||
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
|
"Apktool v" + Androlib.getVersion() + " - a tool for reengineering Android apk files\n" +
|
||||||
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" +
|
"Copyright 2010 Ryszard Wiśniewski <brut.alll@gmail.com>\n" +
|
||||||
"with baksmali/smali " + smaliVersion + " (http://smali.googlecode.com)\n" +
|
"with smali v" + ApktoolProperties.get("smaliVersion") +
|
||||||
"Updated by iBotPeaches (@iBotPeaches) \n" +
|
", and baksmali v" + ApktoolProperties.get("baksmaliVersion") + "\n" +
|
||||||
|
"Updated by iBotPeaches (@iBotPeaches) and yyj \n" +
|
||||||
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
|
"Apache License 2.0 (http://www.apache.org/licenses/LICENSE-2.0)\n" +
|
||||||
"\n" +
|
"\n" +
|
||||||
"Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" +
|
"Usage: apktool [-q|--quiet OR -v|--verbose] COMMAND [...]\n" +
|
||||||
|
@ -46,4 +46,4 @@ public interface AttributeSet {
|
|||||||
//TODO: remove
|
//TODO: remove
|
||||||
int getAttributeValueType(int index);
|
int getAttributeValueType(int index);
|
||||||
int getAttributeValueData(int index);
|
int getAttributeValueData(int index);
|
||||||
}
|
}
|
@ -43,8 +43,6 @@ public class TypedValue {
|
|||||||
/** The <var>data</var> field holds a complex number encoding a fraction
|
/** The <var>data</var> field holds a complex number encoding a fraction
|
||||||
* of a container. */
|
* of a container. */
|
||||||
public static final int TYPE_FRACTION = 0x06;
|
public static final int TYPE_FRACTION = 0x06;
|
||||||
|
|
||||||
public static final int TYPE_LAYOUT = 0x07;
|
|
||||||
|
|
||||||
/** Identifies the start of plain integer values. Any type value
|
/** Identifies the start of plain integer values. Any type value
|
||||||
* from this to {@link #TYPE_LAST_INT} means the
|
* from this to {@link #TYPE_LAST_INT} means the
|
||||||
@ -224,13 +222,42 @@ public class TypedValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
|
if (type >= TYPE_FIRST_COLOR_INT && type <= TYPE_LAST_COLOR_INT) {
|
||||||
return "#" + Integer.toHexString(data);
|
String res =String.format("%08x", data);
|
||||||
|
char[] vals = res.toCharArray();
|
||||||
|
switch (type) {
|
||||||
|
default:
|
||||||
|
case TYPE_INT_COLOR_ARGB8://#AaRrGgBb
|
||||||
|
break;
|
||||||
|
case TYPE_INT_COLOR_RGB8://#FFRrGgBb->#RrGgBb
|
||||||
|
res = res.substring(2);
|
||||||
|
break;
|
||||||
|
case TYPE_INT_COLOR_ARGB4://#AARRGGBB->#ARGB
|
||||||
|
res = new StringBuffer().append(vals[0]).append(vals[2]).append(vals[4]).append(vals[6]).toString();
|
||||||
|
break;
|
||||||
|
case TYPE_INT_COLOR_RGB4://#FFRRGGBB->#RGB
|
||||||
|
res = new StringBuffer().append(vals[2]).append(vals[4]).append(vals[6]).toString();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return "#" + res;
|
||||||
} else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
|
} else if (type >= TYPE_FIRST_INT && type <= TYPE_LAST_INT) {
|
||||||
return Integer.toString(data);
|
String res;
|
||||||
|
switch (type) {
|
||||||
|
default:
|
||||||
|
case TYPE_INT_DEC:
|
||||||
|
res = Integer.toString(data);
|
||||||
|
break;
|
||||||
|
//defined before
|
||||||
|
/*case TYPE_INT_HEX:
|
||||||
|
res = "0x" + Integer.toHexString(data);
|
||||||
|
break;
|
||||||
|
case TYPE_INT_BOOLEAN:
|
||||||
|
res = (data != 0) ? "true":"false";
|
||||||
|
break;*/
|
||||||
|
}
|
||||||
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -24,8 +24,7 @@ import brut.androlib.res.util.ExtFile;
|
|||||||
import brut.androlib.src.SmaliBuilder;
|
import brut.androlib.src.SmaliBuilder;
|
||||||
import brut.androlib.src.SmaliDecoder;
|
import brut.androlib.src.SmaliDecoder;
|
||||||
import brut.common.BrutException;
|
import brut.common.BrutException;
|
||||||
import brut.directory.Directory;
|
import brut.directory.*;
|
||||||
import brut.directory.DirectoryException;
|
|
||||||
import brut.util.BrutIO;
|
import brut.util.BrutIO;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
@ -42,7 +41,11 @@ public class Androlib {
|
|||||||
private final AndrolibResources mAndRes = new AndrolibResources();
|
private final AndrolibResources mAndRes = new AndrolibResources();
|
||||||
|
|
||||||
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
|
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
|
||||||
return mAndRes.getResTable(apkFile);
|
return mAndRes.getResTable(apkFile, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException {
|
||||||
|
return mAndRes.getResTable(apkFile, loadMainPkg);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug)
|
public void decodeSourcesRaw(ExtFile apkFile, File outDir, boolean debug)
|
||||||
@ -78,6 +81,22 @@ public class Androlib {
|
|||||||
new AndrolibJava().decode(apkFile, outDir);
|
new AndrolibJava().decode(apkFile, outDir);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void decodeManifestRaw(ExtFile apkFile, File outDir)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
Directory apk = apkFile.getDirectory();
|
||||||
|
LOGGER.info("Copying raw manifest...");
|
||||||
|
apkFile.getDirectory().copyToDir(outDir, APK_MANIFEST_FILENAMES);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void decodeManifestFull(ExtFile apkFile, File outDir,
|
||||||
|
ResTable resTable) throws AndrolibException {
|
||||||
|
mAndRes.decodeManifest(resTable, apkFile, outDir);
|
||||||
|
}
|
||||||
|
|
||||||
public void decodeResourcesRaw(ExtFile apkFile, File outDir)
|
public void decodeResourcesRaw(ExtFile apkFile, File outDir)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
@ -248,6 +267,8 @@ public class Androlib {
|
|||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
if (! buildResourcesRaw(appDir, forceBuildAll)
|
if (! buildResourcesRaw(appDir, forceBuildAll)
|
||||||
&& ! buildResourcesFull(appDir, forceBuildAll, framework,
|
&& ! buildResourcesFull(appDir, forceBuildAll, framework,
|
||||||
|
usesFramework)
|
||||||
|
&& ! buildManifest(appDir, forceBuildAll, framework,
|
||||||
usesFramework)) {
|
usesFramework)) {
|
||||||
LOGGER.warning("Could not find resources");
|
LOGGER.warning("Could not find resources");
|
||||||
}
|
}
|
||||||
@ -320,6 +341,65 @@ public class Androlib {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean buildManifestRaw(ExtFile appDir, boolean forceBuildAll)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
File apkDir = new File(appDir, APK_DIRNAME);
|
||||||
|
LOGGER.info("Copying raw AndroidManifest.xml...");
|
||||||
|
appDir.getDirectory()
|
||||||
|
.copyToDir(apkDir, APK_MANIFEST_FILENAMES);
|
||||||
|
return true;
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean buildManifest(ExtFile appDir, boolean forceBuildAll,
|
||||||
|
boolean framework, Map<String, Object> usesFramework)
|
||||||
|
throws AndrolibException {
|
||||||
|
try {
|
||||||
|
if (! new File(appDir, "AndroidManifest.xml").exists()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (! forceBuildAll) {
|
||||||
|
LOGGER.info("Checking whether resources has changed...");
|
||||||
|
}
|
||||||
|
File apkDir = new File(appDir, APK_DIRNAME);
|
||||||
|
if (forceBuildAll || isModified(
|
||||||
|
newFiles(APK_MANIFEST_FILENAMES, appDir),
|
||||||
|
newFiles(APK_MANIFEST_FILENAMES, apkDir))) {
|
||||||
|
LOGGER.info("Building AndroidManifest.xml...");
|
||||||
|
|
||||||
|
File apkFile = File.createTempFile("APKTOOL", null);
|
||||||
|
apkFile.delete();
|
||||||
|
|
||||||
|
File ninePatch = new File(appDir, "9patch");
|
||||||
|
if (! ninePatch.exists()) {
|
||||||
|
ninePatch = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
mAndRes.aaptPackage(
|
||||||
|
apkFile,
|
||||||
|
new File(appDir, "AndroidManifest.xml"),
|
||||||
|
null,
|
||||||
|
ninePatch, null, parseUsesFramework(usesFramework),
|
||||||
|
false, framework
|
||||||
|
);
|
||||||
|
|
||||||
|
Directory tmpDir = new ExtFile(apkFile).getDirectory();
|
||||||
|
tmpDir.copyToDir(apkDir, APK_MANIFEST_FILENAMES);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
} catch (AndrolibException ex) {
|
||||||
|
LOGGER.warning("Parse AndroidManifest.xml failed, treat it as raw file.");
|
||||||
|
return buildManifestRaw(appDir, forceBuildAll);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void buildLib(File appDir, boolean forceBuildAll)
|
public void buildLib(File appDir, boolean forceBuildAll)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
File working = new File(appDir, "lib");
|
File working = new File(appDir, "lib");
|
||||||
@ -441,4 +521,6 @@ public class Androlib {
|
|||||||
new String[]{"resources.arsc", "AndroidManifest.xml"};
|
new String[]{"resources.arsc", "AndroidManifest.xml"};
|
||||||
private final static String[] APP_RESOURCES_FILENAMES =
|
private final static String[] APP_RESOURCES_FILENAMES =
|
||||||
new String[]{"AndroidManifest.xml", "res"};
|
new String[]{"AndroidManifest.xml", "res"};
|
||||||
}
|
private final static String[] APK_MANIFEST_FILENAMES =
|
||||||
|
new String[]{"AndroidManifest.xml"};
|
||||||
|
}
|
@ -26,10 +26,7 @@ import brut.common.BrutException;
|
|||||||
import brut.directory.DirectoryException;
|
import brut.directory.DirectoryException;
|
||||||
import brut.util.OS;
|
import brut.util.OS;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.*;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.Set;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
@ -92,6 +89,7 @@ public class ApkDecoder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasResources()) {
|
if (hasResources()) {
|
||||||
switch (mDecodeResources) {
|
switch (mDecodeResources) {
|
||||||
case DECODE_RESOURCES_NONE:
|
case DECODE_RESOURCES_NONE:
|
||||||
@ -102,7 +100,22 @@ public class ApkDecoder {
|
|||||||
getResTable());
|
getResTable());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// if there's no resources.asrc, decode the manifest without looking up
|
||||||
|
// attribute references
|
||||||
|
if (hasManifest()) {
|
||||||
|
switch (mDecodeResources) {
|
||||||
|
case DECODE_RESOURCES_NONE:
|
||||||
|
mAndrolib.decodeManifestRaw(mApkFile, outDir);
|
||||||
|
break;
|
||||||
|
case DECODE_RESOURCES_FULL:
|
||||||
|
mAndrolib.decodeManifestFull(mApkFile, outDir,
|
||||||
|
getResTable());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mAndrolib.decodeRawFiles(mApkFile, outDir);
|
mAndrolib.decodeRawFiles(mApkFile, outDir);
|
||||||
writeMetaFile();
|
writeMetaFile();
|
||||||
}
|
}
|
||||||
@ -143,12 +156,14 @@ public class ApkDecoder {
|
|||||||
|
|
||||||
public ResTable getResTable() throws AndrolibException {
|
public ResTable getResTable() throws AndrolibException {
|
||||||
if (mResTable == null) {
|
if (mResTable == null) {
|
||||||
if (! hasResources()) {
|
boolean hasResources = hasResources();
|
||||||
|
boolean hasManifest = hasManifest();
|
||||||
|
if (! (hasManifest || hasResources)) {
|
||||||
throw new AndrolibException(
|
throw new AndrolibException(
|
||||||
"Apk doesn't containt resources.arsc file");
|
"Apk doesn't contain either AndroidManifest.xml file or resources.arsc file");
|
||||||
}
|
}
|
||||||
AndrolibResources.sKeepBroken = mKeepBrokenResources;
|
AndrolibResources.sKeepBroken = mKeepBrokenResources;
|
||||||
mResTable = mAndrolib.getResTable(mApkFile);
|
mResTable = mAndrolib.getResTable(mApkFile, hasResources);
|
||||||
mResTable.setFrameTag(mFrameTag);
|
mResTable.setFrameTag(mFrameTag);
|
||||||
}
|
}
|
||||||
return mResTable;
|
return mResTable;
|
||||||
@ -162,6 +177,14 @@ public class ApkDecoder {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean hasManifest() throws AndrolibException {
|
||||||
|
try {
|
||||||
|
return mApkFile.getDirectory().containsFile("AndroidManifest.xml");
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public boolean hasResources() throws AndrolibException {
|
public boolean hasResources() throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
return mApkFile.getDirectory().containsFile("resources.arsc");
|
return mApkFile.getDirectory().containsFile("resources.arsc");
|
||||||
@ -190,7 +213,7 @@ public class ApkDecoder {
|
|||||||
meta.put("version", Androlib.getVersion());
|
meta.put("version", Androlib.getVersion());
|
||||||
meta.put("apkFileName", mApkFile.getName());
|
meta.put("apkFileName", mApkFile.getName());
|
||||||
|
|
||||||
if (mDecodeResources != DECODE_RESOURCES_NONE && hasResources()) {
|
if (mDecodeResources != DECODE_RESOURCES_NONE && (hasManifest() || hasResources())) {
|
||||||
meta.put("isFrameworkApk",
|
meta.put("isFrameworkApk",
|
||||||
Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable())));
|
Boolean.valueOf(mAndrolib.isFrameworkApk(getResTable())));
|
||||||
putUsesFramework(meta);
|
putUsesFramework(meta);
|
||||||
@ -234,4 +257,4 @@ public class ApkDecoder {
|
|||||||
private boolean mForceDelete = false;
|
private boolean mForceDelete = false;
|
||||||
private String mFrameTag;
|
private String mFrameTag;
|
||||||
private boolean mKeepBrokenResources = false;
|
private boolean mKeepBrokenResources = false;
|
||||||
}
|
}
|
@ -21,6 +21,9 @@ import java.io.InputStream;
|
|||||||
import java.util.Properties;
|
import java.util.Properties;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import org.jf.baksmali.baksmali;
|
||||||
|
import org.jf.smali.main;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
@ -46,10 +49,29 @@ public class ApktoolProperties {
|
|||||||
} catch (IOException ex) {
|
} catch (IOException ex) {
|
||||||
LOGGER.warning("Can't load properties.");
|
LOGGER.warning("Can't load properties.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InputStream templateStream = baksmali.class.getClassLoader().getResourceAsStream("baksmali.properties");
|
||||||
|
Properties properties = new Properties();
|
||||||
|
String version = "(unknown)";
|
||||||
|
try {
|
||||||
|
properties.load(templateStream);
|
||||||
|
version = properties.getProperty("application.version");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
}
|
||||||
|
sProps.put("baksmaliVersion", version);
|
||||||
|
templateStream = main.class.getClassLoader().getResourceAsStream("smali.properties");
|
||||||
|
properties = new Properties();
|
||||||
|
version = "(unknown)";
|
||||||
|
try {
|
||||||
|
properties.load(templateStream);
|
||||||
|
version = properties.getProperty("application.version");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
}
|
||||||
|
sProps.put("smaliVersion", version);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Properties sProps;
|
private static Properties sProps;
|
||||||
|
|
||||||
private static final Logger LOGGER =
|
private static final Logger LOGGER =
|
||||||
Logger.getLogger(ApktoolProperties.class.getName());
|
Logger.getLogger(ApktoolProperties.class.getName());
|
||||||
}
|
}
|
@ -39,8 +39,14 @@ import org.xmlpull.v1.XmlSerializer;
|
|||||||
*/
|
*/
|
||||||
final public class AndrolibResources {
|
final public class AndrolibResources {
|
||||||
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
|
public ResTable getResTable(ExtFile apkFile) throws AndrolibException {
|
||||||
|
return getResTable(apkFile, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ResTable getResTable(ExtFile apkFile, boolean loadMainPkg) throws AndrolibException {
|
||||||
ResTable resTable = new ResTable(this);
|
ResTable resTable = new ResTable(this);
|
||||||
loadMainPkg(resTable, apkFile);
|
if (loadMainPkg) {
|
||||||
|
loadMainPkg(resTable, apkFile);
|
||||||
|
}
|
||||||
return resTable;
|
return resTable;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -97,6 +103,36 @@ final public class AndrolibResources {
|
|||||||
return pkg;
|
return pkg;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void decodeManifest(ResTable resTable, ExtFile apkFile, File outDir)
|
||||||
|
throws AndrolibException {
|
||||||
|
|
||||||
|
Duo<ResFileDecoder, AXmlResourceParser> duo = getManifestFileDecoder();
|
||||||
|
ResFileDecoder fileDecoder = duo.m1;
|
||||||
|
|
||||||
|
|
||||||
|
// Set ResAttrDecoder
|
||||||
|
|
||||||
|
duo.m2.setAttrDecoder(new ResAttrDecoder());
|
||||||
|
|
||||||
|
ResAttrDecoder attrDecoder = duo.m2.getAttrDecoder();
|
||||||
|
// Fake ResPackage
|
||||||
|
attrDecoder.setCurrentPackage(new ResPackage(resTable, 0, null));
|
||||||
|
|
||||||
|
Directory inApk, out;
|
||||||
|
try {
|
||||||
|
inApk = apkFile.getDirectory();
|
||||||
|
out = new FileDirectory(outDir);
|
||||||
|
|
||||||
|
LOGGER.info("Decoding AndroidManifest.xml with only framework resources...");
|
||||||
|
fileDecoder.decode(
|
||||||
|
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
|
||||||
|
"xml");
|
||||||
|
|
||||||
|
} catch (DirectoryException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void decode(ResTable resTable, ExtFile apkFile, File outDir)
|
public void decode(ResTable resTable, ExtFile apkFile, File outDir)
|
||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
|
Duo<ResFileDecoder, AXmlResourceParser> duo = getResFileDecoder();
|
||||||
@ -111,6 +147,7 @@ final public class AndrolibResources {
|
|||||||
inApk = apkFile.getDirectory();
|
inApk = apkFile.getDirectory();
|
||||||
out = new FileDirectory(outDir);
|
out = new FileDirectory(outDir);
|
||||||
|
|
||||||
|
LOGGER.info("Decoding AndroidManifest.xml with resources...");
|
||||||
fileDecoder.decode(
|
fileDecoder.decode(
|
||||||
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
|
inApk, "AndroidManifest.xml", out, "AndroidManifest.xml",
|
||||||
"xml");
|
"xml");
|
||||||
@ -238,6 +275,19 @@ final public class AndrolibResources {
|
|||||||
new ResFileDecoder(decoders), axmlParser);
|
new ResFileDecoder(decoders), axmlParser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Duo<ResFileDecoder, AXmlResourceParser> getManifestFileDecoder() {
|
||||||
|
ResStreamDecoderContainer decoders =
|
||||||
|
new ResStreamDecoderContainer();
|
||||||
|
|
||||||
|
AXmlResourceParser axmlParser = new AXmlResourceParser();
|
||||||
|
|
||||||
|
decoders.setDecoder("xml",
|
||||||
|
new XmlPullStreamDecoder(axmlParser, getResXmlSerializer()));
|
||||||
|
|
||||||
|
return new Duo<ResFileDecoder, AXmlResourceParser>(
|
||||||
|
new ResFileDecoder(decoders), axmlParser);
|
||||||
|
}
|
||||||
|
|
||||||
public ExtMXSerializer getResXmlSerializer() {
|
public ExtMXSerializer getResXmlSerializer() {
|
||||||
ExtMXSerializer serial = new ExtMXSerializer();
|
ExtMXSerializer serial = new ExtMXSerializer();
|
||||||
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION
|
serial.setProperty(ExtXmlSerializer.PROPERTY_SERIALIZER_INDENTATION
|
||||||
@ -491,4 +541,4 @@ final public class AndrolibResources {
|
|||||||
|
|
||||||
private final static Logger LOGGER =
|
private final static Logger LOGGER =
|
||||||
Logger.getLogger(AndrolibResources.class.getName());
|
Logger.getLogger(AndrolibResources.class.getName());
|
||||||
}
|
}
|
@ -196,6 +196,10 @@ public class ResConfigFlags {
|
|||||||
case UI_MODE_TYPE_DESK:
|
case UI_MODE_TYPE_DESK:
|
||||||
ret.append("-desk");
|
ret.append("-desk");
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case UI_MODE_TYPE_APPLIANCE:
|
||||||
|
ret.append("-appliance");
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
switch (uiMode & MASK_UI_MODE_NIGHT) {
|
switch (uiMode & MASK_UI_MODE_NIGHT) {
|
||||||
case UI_MODE_NIGHT_YES:
|
case UI_MODE_NIGHT_YES:
|
||||||
@ -410,6 +414,7 @@ public class ResConfigFlags {
|
|||||||
public final static byte UI_MODE_TYPE_DESK = 0x02;
|
public final static byte UI_MODE_TYPE_DESK = 0x02;
|
||||||
public final static byte UI_MODE_TYPE_CAR = 0x03;
|
public final static byte UI_MODE_TYPE_CAR = 0x03;
|
||||||
public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
|
public final static byte UI_MODE_TYPE_TELEVISION = 0x04;
|
||||||
|
public final static byte UI_MODE_TYPE_APPLIANCE = 0x05;
|
||||||
|
|
||||||
public final static byte MASK_UI_MODE_NIGHT = 0x30;
|
public final static byte MASK_UI_MODE_NIGHT = 0x30;
|
||||||
public final static byte UI_MODE_NIGHT_ANY = 0x00;
|
public final static byte UI_MODE_NIGHT_ANY = 0x00;
|
||||||
|
@ -16,16 +16,22 @@
|
|||||||
|
|
||||||
package brut.androlib.res.data.value;
|
package brut.androlib.res.data.value;
|
||||||
|
|
||||||
|
import android.util.TypedValue;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
* @author Ryszard Wiśniewski <brut.alll@gmail.com>
|
||||||
*/
|
*/
|
||||||
public class ResColorValue extends ResIntValue {
|
public class ResColorValue extends ResIntValue {
|
||||||
public ResColorValue(int value, String rawValue) {
|
private int type;
|
||||||
|
|
||||||
|
public ResColorValue(int value, String rawValue, int type) {
|
||||||
super(value, rawValue, "color");
|
super(value, rawValue, "color");
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected String encodeAsResXml() {
|
protected String encodeAsResXml() {
|
||||||
return String.format("#%08x", mValue);
|
return TypedValue.coerceToString(type, mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,6 +16,7 @@
|
|||||||
|
|
||||||
package brut.androlib.res.data.value;
|
package brut.androlib.res.data.value;
|
||||||
|
|
||||||
|
import android.util.TypedValue;
|
||||||
import brut.androlib.AndrolibException;
|
import brut.androlib.AndrolibException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -23,9 +24,11 @@ import brut.androlib.AndrolibException;
|
|||||||
*/
|
*/
|
||||||
public class ResIntValue extends ResScalarValue {
|
public class ResIntValue extends ResScalarValue {
|
||||||
protected final int mValue;
|
protected final int mValue;
|
||||||
|
private int type;
|
||||||
|
|
||||||
public ResIntValue(int value, String rawValue) {
|
public ResIntValue(int value, String rawValue, int type) {
|
||||||
this(value, rawValue, "integer");
|
this(value, rawValue, "integer");
|
||||||
|
this.type = type;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ResIntValue(int value, String rawValue, String type) {
|
public ResIntValue(int value, String rawValue, String type) {
|
||||||
@ -38,6 +41,6 @@ public class ResIntValue extends ResScalarValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected String encodeAsResXml() throws AndrolibException {
|
protected String encodeAsResXml() throws AndrolibException {
|
||||||
return String.valueOf(mValue);
|
return TypedValue.coerceToString(type, mValue);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -16,10 +16,9 @@
|
|||||||
|
|
||||||
package brut.androlib.res.data.value;
|
package brut.androlib.res.data.value;
|
||||||
|
|
||||||
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
|
||||||
import brut.androlib.AndrolibException;
|
import brut.androlib.AndrolibException;
|
||||||
import brut.androlib.res.data.ResResource;
|
import brut.androlib.res.data.ResResource;
|
||||||
import brut.androlib.res.xml.ResXmlEncoders;
|
import brut.androlib.res.xml.ResValuesXmlSerializable;
|
||||||
import brut.util.Duo;
|
import brut.util.Duo;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
@ -52,16 +51,14 @@ public class ResPluralsValue extends ResBagValue implements ResValuesXmlSerializ
|
|||||||
serializer.startTag(null, "item");
|
serializer.startTag(null, "item");
|
||||||
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
|
serializer.attribute(null, "quantity", QUANTITY_MAP[i]);
|
||||||
|
|
||||||
String item2 = item.encodeAsResXmlValue();
|
String rawValue = item.encodeAsResXmlValueExt();
|
||||||
|
//AAPT don`t parse formatted for item tag(only for string and string-array tag),
|
||||||
/*
|
// so adding "formatted='fasle'" is useless.
|
||||||
* peaches fix regarding formatted=false
|
/*if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
|
||||||
*/
|
|
||||||
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(item2)) {
|
|
||||||
serializer.attribute(null, "formatted", "false");
|
serializer.attribute(null, "formatted", "false");
|
||||||
}
|
}*/
|
||||||
serializer.text(item2);
|
serializer.text(rawValue);
|
||||||
|
|
||||||
serializer.endTag(null, "item");
|
serializer.endTag(null, "item");
|
||||||
}
|
}
|
||||||
serializer.endTag(null, "plurals");
|
serializer.endTag(null, "plurals");
|
||||||
|
@ -20,6 +20,7 @@ import brut.androlib.res.xml.ResValuesXmlSerializable;
|
|||||||
import brut.androlib.res.xml.ResXmlEncodable;
|
import brut.androlib.res.xml.ResXmlEncodable;
|
||||||
import brut.androlib.AndrolibException;
|
import brut.androlib.AndrolibException;
|
||||||
import brut.androlib.res.data.ResResource;
|
import brut.androlib.res.data.ResResource;
|
||||||
|
import brut.androlib.res.xml.ResXmlEncoders;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import org.xmlpull.v1.XmlSerializer;
|
import org.xmlpull.v1.XmlSerializer;
|
||||||
|
|
||||||
@ -53,6 +54,38 @@ public abstract class ResScalarValue extends ResValue
|
|||||||
}
|
}
|
||||||
return encodeAsResXml();
|
return encodeAsResXml();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String encodeAsResXmlValueExt() throws AndrolibException {
|
||||||
|
String rawValue = mRawValue;
|
||||||
|
if (rawValue != null) {
|
||||||
|
if (ResXmlEncoders.hasMultipleNonPositionalSubstitutions(rawValue)) {
|
||||||
|
int count = 1;
|
||||||
|
StringBuffer result = new StringBuffer();
|
||||||
|
String tmp1[] = rawValue.split("%%", -1);
|
||||||
|
int tmp1_sz = tmp1.length;
|
||||||
|
for(int i=0;i<tmp1_sz;i++) {
|
||||||
|
String cur1 = tmp1[i];
|
||||||
|
String tmp2[] = cur1.split("%", -1);
|
||||||
|
int tmp2_sz = tmp2.length;
|
||||||
|
for(int j=0;j<tmp2_sz;j++) {
|
||||||
|
String cur2 = tmp2[j];
|
||||||
|
result.append(cur2);
|
||||||
|
if(j != (tmp2_sz-1)) {
|
||||||
|
result.append('%').append(count).append('$');
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(i != (tmp1_sz-1)) {
|
||||||
|
result.append("%%");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rawValue = result.toString();
|
||||||
|
}
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
return encodeAsResXml();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
|
public void serializeToResValuesXml(XmlSerializer serializer, ResResource res)
|
||||||
throws IOException, AndrolibException {
|
throws IOException, AndrolibException {
|
||||||
|
@ -52,11 +52,11 @@ public class ResValueFactory {
|
|||||||
|
|
||||||
if (type >= TypedValue.TYPE_FIRST_COLOR_INT
|
if (type >= TypedValue.TYPE_FIRST_COLOR_INT
|
||||||
&& type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
&& type <= TypedValue.TYPE_LAST_COLOR_INT) {
|
||||||
return new ResColorValue(value, rawValue);
|
return new ResColorValue(value, rawValue, type);
|
||||||
}
|
}
|
||||||
if (type >= TypedValue.TYPE_FIRST_INT
|
if (type >= TypedValue.TYPE_FIRST_INT
|
||||||
&& type <= TypedValue.TYPE_LAST_INT) {
|
&& type <= TypedValue.TYPE_LAST_INT) {
|
||||||
return new ResIntValue(value, rawValue);
|
return new ResIntValue(value, rawValue, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new AndrolibException("Invalid value type: "+ type);
|
throw new AndrolibException("Invalid value type: "+ type);
|
||||||
@ -98,4 +98,4 @@ public class ResValueFactory {
|
|||||||
boolean theme) {
|
boolean theme) {
|
||||||
return new ResReferenceValue(mPackage, resID, rawValue, theme);
|
return new ResReferenceValue(mPackage, resID, rawValue, theme);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -325,6 +325,10 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
valueData
|
valueData
|
||||||
), ex);
|
), ex);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
if (valueType==TypedValue.TYPE_STRING) {
|
||||||
|
return m_strings.getString(valueRaw);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return TypedValue.coerceToString(valueType, valueData);
|
return TypedValue.coerceToString(valueType, valueData);
|
||||||
@ -370,7 +374,7 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
public String getAttributeValue(String namespace, String attribute) {
|
public String getAttributeValue(String namespace, String attribute) {
|
||||||
int index = findAttribute(namespace, attribute);
|
int index = findAttribute(namespace, attribute);
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
return null;
|
return "";
|
||||||
}
|
}
|
||||||
return getAttributeValue(index);
|
return getAttributeValue(index);
|
||||||
}
|
}
|
||||||
@ -755,7 +759,7 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
if (m_event != START_TAG) {
|
if (m_event != START_TAG) {
|
||||||
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
|
throw new IndexOutOfBoundsException("Current event is not START_TAG.");
|
||||||
}
|
}
|
||||||
int offset = index * 5;
|
int offset = index * ATTRIBUTE_LENGHT;
|
||||||
if (offset >= m_attributes.length) {
|
if (offset >= m_attributes.length) {
|
||||||
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ").");
|
throw new IndexOutOfBoundsException("Invalid attribute index (" + index + ").");
|
||||||
}
|
}
|
||||||
@ -960,4 +964,4 @@ public class AXmlResourceParser implements XmlResourceParser {
|
|||||||
CHUNK_XML_END_TAG = 0x00100103,
|
CHUNK_XML_END_TAG = 0x00100103,
|
||||||
CHUNK_XML_TEXT = 0x00100104,
|
CHUNK_XML_TEXT = 0x00100104,
|
||||||
CHUNK_XML_LAST = 0x00100104;
|
CHUNK_XML_LAST = 0x00100104;
|
||||||
}
|
}
|
@ -34,35 +34,31 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
|||||||
throws AndrolibException {
|
throws AndrolibException {
|
||||||
try {
|
try {
|
||||||
byte[] data = IOUtils.toByteArray(in);
|
byte[] data = IOUtils.toByteArray(in);
|
||||||
|
NinePatch np = getNinePatch(data);
|
||||||
|
|
||||||
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
|
BufferedImage im = ImageIO.read(new ByteArrayInputStream(data));
|
||||||
int w = im.getWidth(), h = im.getHeight();
|
int w = im.getWidth(), h = im.getHeight();
|
||||||
|
|
||||||
BufferedImage im2 = new BufferedImage(w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR);
|
BufferedImage im2 = new BufferedImage(
|
||||||
|
w + 2, h + 2, BufferedImage.TYPE_4BYTE_ABGR);
|
||||||
Raster src = im.getRaster();
|
Raster src = im.getRaster();
|
||||||
|
|
||||||
NinePatch np = getNinePatch(data);
|
|
||||||
WritableRaster dst = im2.getRaster();
|
WritableRaster dst = im2.getRaster();
|
||||||
int nbands = im.getSampleModel().getNumBands();
|
int nbands = im.getSampleModel().getNumBands();
|
||||||
int[] bands = new int[4];
|
int[] bands = new int[4];
|
||||||
if (nbands == 2) {
|
if (nbands == 2) {
|
||||||
bands[0] = bands[1] = bands[2] = 0;
|
bands[0] = bands[1] = bands[2] = 0;
|
||||||
bands[3] = 1;
|
bands[3] = 1;
|
||||||
} else {
|
} else {
|
||||||
bands[0] = 0;
|
bands[0] = 0; bands[1] = 1; bands[2] = 2; bands[3] = 3;
|
||||||
bands[1] = 1;
|
|
||||||
bands[2] = 2;
|
|
||||||
bands[3] = 3;
|
|
||||||
}
|
}
|
||||||
int[] band = null;
|
int[] band = null;
|
||||||
for (int y = 0; y < h; y++) {
|
for (int y = 0; y < h; y++) {
|
||||||
for (int bi = 0; bi < 4; bi++) {
|
for (int bi = 0; bi < 4; bi++) {
|
||||||
band = src.getSamples(0, y, w, 1, bands[bi], band);
|
band = src.getSamples(0, y, w, 1, bands[bi], band);
|
||||||
dst.setSamples(1, y + 1, w, 1, bi, band);
|
dst.setSamples(1, y + 1, w, 1, bi, band);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
drawHLine(im2, h + 1, np.padLeft + 1, w - np.padRight);
|
||||||
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
drawVLine(im2, w + 1, np.padTop + 1, h - np.padBottom);
|
||||||
|
|
||||||
@ -154,4 +150,4 @@ public class Res9patchStreamDecoder implements ResStreamDecoder {
|
|||||||
xDivs, yDivs);
|
xDivs, yDivs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -94,19 +94,28 @@ public class ResFileDecoder {
|
|||||||
|
|
||||||
public void decode(Directory inDir, String inFileName, Directory outDir,
|
public void decode(Directory inDir, String inFileName, Directory outDir,
|
||||||
String outFileName, String decoder) throws AndrolibException {
|
String outFileName, String decoder) throws AndrolibException {
|
||||||
|
InputStream in = null;
|
||||||
|
OutputStream out = null;
|
||||||
try {
|
try {
|
||||||
InputStream in = inDir.getFileInput(inFileName);
|
in = inDir.getFileInput(inFileName);
|
||||||
OutputStream out = outDir.getFileOutput(outFileName);
|
out = outDir.getFileOutput(outFileName);
|
||||||
mDecoders.decode(in, out, decoder);
|
mDecoders.decode(in, out, decoder);
|
||||||
in.close();
|
|
||||||
out.close();
|
|
||||||
} catch (IOException ex) {
|
|
||||||
throw new AndrolibException(ex);
|
|
||||||
} catch (DirectoryException ex) {
|
} catch (DirectoryException ex) {
|
||||||
throw new AndrolibException(ex);
|
throw new AndrolibException(ex);
|
||||||
|
} finally {
|
||||||
|
try{
|
||||||
|
if (in != null) {
|
||||||
|
in.close();
|
||||||
|
}
|
||||||
|
if (out != null) {
|
||||||
|
out.close();
|
||||||
|
}
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AndrolibException(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final static Logger LOGGER =
|
private final static Logger LOGGER =
|
||||||
Logger.getLogger(ResFileDecoder.class.getName());
|
Logger.getLogger(ResFileDecoder.class.getName());
|
||||||
}
|
}
|
@ -316,4 +316,4 @@ public final class LEDataInputStream implements DataInput
|
|||||||
{
|
{
|
||||||
return dis.skipBytes( n );
|
return dis.skipBytes( n );
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in New Issue
Block a user