Skip to content

Commit 4ae3196

Browse files
committed
New and aapt features for <info>
1 parent 59a4044 commit 4ae3196

File tree

8 files changed

+274
-14
lines changed

8 files changed

+274
-14
lines changed

libs/ARSCLib.jar

137 Bytes
Binary file not shown.

src/main/java/com/reandroid/apkeditor/info/Info.java

Lines changed: 60 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,16 @@
1616
package com.reandroid.apkeditor.info;
1717

1818
import com.reandroid.apk.ApkModule;
19+
import com.reandroid.apk.ResFile;
1920
import com.reandroid.apkeditor.CommandExecutor;
2021
import com.reandroid.apkeditor.Util;
2122
import com.reandroid.app.AndroidManifest;
23+
import com.reandroid.archive.InputSource;
2224
import com.reandroid.arsc.chunk.PackageBlock;
2325
import com.reandroid.arsc.chunk.TableBlock;
2426
import com.reandroid.arsc.chunk.xml.AndroidManifestBlock;
2527
import com.reandroid.arsc.chunk.xml.ResXmlAttribute;
28+
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
2629
import com.reandroid.arsc.chunk.xml.ResXmlElement;
2730
import com.reandroid.arsc.coder.EncodeResult;
2831
import com.reandroid.arsc.coder.ReferenceString;
@@ -94,14 +97,20 @@ private void print(ApkModule apkModule) throws IOException {
9497
printResources(apkModule);
9598
printDex(apkModule);
9699
printSignatures(apkModule);
100+
101+
printXmlTree(apkModule);
102+
printXmlStrings(apkModule);
103+
listFiles(apkModule);
104+
listXmlFiles(apkModule);
97105
}
98106
private void printSourceFile() throws IOException {
99107
InfoOptions options = getOptions();
100108
if(options.outputFile == null){
101109
return;
102110
}
103111
if(options.verbose || !options.resources){
104-
getInfoWriter().writeNameValue("source-file",
112+
InfoWriter infoWriter = getInfoWriter();
113+
infoWriter.writeNameValue("source-file",
105114
options.inputFile.getAbsolutePath());
106115
}
107116
}
@@ -346,6 +355,56 @@ private void printAppClass(ApkModule apkModule) throws IOException {
346355
getInfoWriter().writeNameValue("application-class", value);
347356
}
348357
}
358+
private void printXmlStrings(ApkModule apkModule) throws IOException {
359+
InfoOptions options = getOptions();
360+
String xmlStrings = options.xmlStrings;
361+
if (xmlStrings == null) {
362+
return;
363+
}
364+
InfoWriter infoWriter = getInfoWriter();
365+
ResXmlDocument document = apkModule.loadResXmlDocument(xmlStrings);
366+
document.setApkFile(null);
367+
document.setPackageBlock(null);
368+
infoWriter.writeStringPool(document.getStringPool());
369+
}
370+
private void printXmlTree(ApkModule apkModule) throws IOException {
371+
InfoOptions options = getOptions();
372+
InfoWriter infoWriter = getInfoWriter();
373+
for (String path : options.xmlTree) {
374+
logMessage("Writing: " + path);
375+
ResXmlDocument document = apkModule.loadResXmlDocument(path);
376+
document.setApkFile(null);
377+
document.setPackageBlock(null);
378+
infoWriter.writeXmlDocument(path, document);
379+
}
380+
}
381+
private void listFiles(ApkModule apkModule) throws IOException {
382+
InfoOptions options = getOptions();
383+
if (!options.listFiles) {
384+
return;
385+
}
386+
InputSource[] inputSources = apkModule.getInputSources();
387+
int count = inputSources.length;
388+
String[] names = new String[count];
389+
for (int i = 0; i < count; i++) {
390+
names[i] = inputSources[i].getAlias();
391+
}
392+
getInfoWriter().writeArray("Files", names);
393+
}
394+
private void listXmlFiles(ApkModule apkModule) throws IOException {
395+
InfoOptions options = getOptions();
396+
if (!options.listXmlFiles) {
397+
return;
398+
}
399+
List<ResFile> resFileList = apkModule.listResFiles();
400+
List<String> names = new ArrayList<>();
401+
for (ResFile resFile : resFileList) {
402+
if(resFile.isBinaryXml()) {
403+
names.add(resFile.getFilePath());
404+
}
405+
}
406+
getInfoWriter().writeArray("CompiledXmlFiles", names.toArray(new String[0]));
407+
}
349408
private String getValueOfName(ResXmlElement element){
350409
ResXmlAttribute attribute = element
351410
.searchAttributeByResourceId(AndroidManifest.ID_name);

src/main/java/com/reandroid/apkeditor/info/InfoOptions.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,14 @@ public class InfoOptions extends OptionsWithFramework {
7373
public boolean signatures = false;
7474
@OptionArg(name = "-signatures-base64", description = "info_signatures_base64", flag = true)
7575
public boolean signatures_base64 = false;
76+
@OptionArg(name = "-xmlstrings", description = "info_xml_strings")
77+
public String xmlStrings;
78+
@OptionArg(name = "-xmltree", description = "info_xml_tree")
79+
public final List<String> xmlTree = new ArrayList<>();
80+
@OptionArg(name = "-list-files", description = "info_list_files", flag = true)
81+
public boolean listFiles = false;
82+
@OptionArg(name = "-list-xml-files", description = "info_list_xml_files", flag = true)
83+
public boolean listXmlFiles = false;
7684

7785
public InfoOptions(){
7886
super();
@@ -139,8 +147,9 @@ private void initializeDefaults(){
139147
private boolean isDefault() {
140148
boolean flagsChanged = activities || appClass || appIcon || appName || appRoundIcon ||
141149
dex || minSdkVersion || packageName || permissions || targetSdkVersion ||
142-
resources || signatures || signatures_base64 || versionCode || versionName;
150+
resources || signatures || signatures_base64 || versionCode || versionName ||
151+
listFiles || listXmlFiles || xmlStrings != null;
143152

144-
return !flagsChanged && resList.isEmpty() && typeFilterList.isEmpty();
153+
return !flagsChanged && resList.isEmpty() && typeFilterList.isEmpty() && xmlTree.isEmpty();
145154
}
146155
}

src/main/java/com/reandroid/apkeditor/info/InfoWriter.java

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,11 @@
1818
import com.reandroid.archive.block.ApkSignatureBlock;
1919
import com.reandroid.archive.block.CertificateBlock;
2020
import com.reandroid.arsc.chunk.PackageBlock;
21+
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
2122
import com.reandroid.arsc.coder.ValueCoder;
2223
import com.reandroid.arsc.container.SpecTypePair;
2324
import com.reandroid.arsc.model.ResourceEntry;
25+
import com.reandroid.arsc.pool.StringPool;
2426
import com.reandroid.dex.model.DexDirectory;
2527
import com.reandroid.dex.model.DexFile;
2628
import com.reandroid.utils.HexUtil;
@@ -58,6 +60,10 @@ public void writeDexInfo(DexDirectory dexDirectory) throws IOException {
5860
}
5961
}
6062

63+
public void writeStringPool(StringPool<?> stringPool) throws IOException {
64+
65+
}
66+
public abstract void writeXmlDocument(String sourcePath, ResXmlDocument xmlDocument) throws IOException;
6167
public abstract void writeCertificates(List<CertificateBlock> certificateList, boolean base64) throws IOException;
6268
public abstract void writeDexInfo(DexFile dexFile, boolean writeSectionInfo) throws IOException;
6369
public abstract void writeResources(ResourceEntry resourceEntry, boolean writeEntries) throws IOException;
@@ -86,13 +92,6 @@ static String toString(Object obj){
8692
}
8793
return null;
8894
}
89-
static String getValueAsString(Entry entry){
90-
ResValue resValue = entry.getResValue();
91-
if(resValue == null){
92-
return "";
93-
}
94-
return getValueAsString(resValue);
95-
}
9695
static String getValueAsString(Value value){
9796
ValueType valueType = value.getValueType();
9897
if(valueType == ValueType.STRING){
@@ -114,6 +113,12 @@ static String toBase64(byte[] bytes) {
114113
return Base64.getEncoder().encodeToString(bytes);
115114
}
116115

116+
static void writeSpaces(Writer writer, int amount) throws IOException {
117+
for (int i = 0; i < amount; i ++) {
118+
writer.append(' ');
119+
}
120+
}
121+
117122
static final String TAG_RES_PACKAGES = "resource-packages";
118123
static final String TAG_PUBLIC = "public";
119124
static final String TAG_RESOURCES = "resources";

src/main/java/com/reandroid/apkeditor/info/InfoWriterJson.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import com.reandroid.archive.block.CertificateBlock;
1919
import com.reandroid.arsc.array.ResValueMapArray;
2020
import com.reandroid.arsc.chunk.PackageBlock;
21+
import com.reandroid.arsc.chunk.xml.ResXmlDocument;
2122
import com.reandroid.arsc.container.SpecTypePair;
2223
import com.reandroid.arsc.model.ResourceEntry;
2324
import com.reandroid.arsc.value.Entry;
@@ -46,6 +47,14 @@ public InfoWriterJson(Writer writer) {
4647
this.mJsonWriter = jsonWriter;
4748
}
4849

50+
@Override
51+
public void writeXmlDocument(String sourcePath, ResXmlDocument xmlDocument) throws IOException {
52+
JSONWriter jsonWriter = mJsonWriter.object();
53+
jsonWriter.key("source_path").value(sourcePath);
54+
jsonWriter.key("document").value(xmlDocument.toJson());
55+
jsonWriter.endObject();
56+
}
57+
4958
@Override
5059
public void writeCertificates(List<CertificateBlock> certificateList, boolean base64) throws IOException {
5160
JSONWriter jsonWriter = mJsonWriter.object()
@@ -220,6 +229,7 @@ public void writeNameValue(String name, Object value) throws IOException {
220229
.endObject();
221230
getWriter().flush();
222231
}
232+
223233
@Override
224234
public void flush() throws IOException {
225235
Writer writer = getWriter();

src/main/java/com/reandroid/apkeditor/info/InfoWriterText.java

Lines changed: 143 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,14 @@
1818
import com.reandroid.archive.block.CertificateBlock;
1919
import com.reandroid.arsc.array.ResValueMapArray;
2020
import com.reandroid.arsc.chunk.PackageBlock;
21+
import com.reandroid.arsc.chunk.xml.*;
2122
import com.reandroid.arsc.container.SpecTypePair;
23+
import com.reandroid.arsc.header.StringPoolHeader;
24+
import com.reandroid.arsc.item.StringItem;
2225
import com.reandroid.arsc.model.ResourceEntry;
23-
import com.reandroid.arsc.value.Entry;
24-
import com.reandroid.arsc.value.ResTableMapEntry;
25-
import com.reandroid.arsc.value.ResValue;
26-
import com.reandroid.arsc.value.ResValueMap;
26+
import com.reandroid.arsc.pool.StringPool;
27+
import com.reandroid.arsc.value.*;
28+
import com.reandroid.common.Namespace;
2729
import com.reandroid.dex.model.DexFile;
2830
import com.reandroid.dex.sections.MapItem;
2931
import com.reandroid.dex.sections.MapList;
@@ -44,6 +46,140 @@ public InfoWriterText(Writer writer) {
4446
super(writer);
4547
}
4648

49+
@Override
50+
public void writeStringPool(StringPool<?> stringPool) throws IOException {
51+
Writer writer = getWriter();
52+
writer.write("String pool of ");
53+
writer.write(Integer.toString(stringPool.size()));
54+
writer.write(" unique ");
55+
if (stringPool.isUtf8()) {
56+
writer.write("UTF-8 ");
57+
} else {
58+
writer.write("UTF-16 ");
59+
}
60+
StringPoolHeader header = stringPool.getHeaderBlock();
61+
if (!header.isSorted()) {
62+
writer.write("non-");
63+
}
64+
writer.write("sorted strings, ");
65+
writer.write(Integer.toString(stringPool.size()));
66+
writer.write(" entries and ");
67+
writer.write(Integer.toString(stringPool.countStyles()));
68+
writer.write(" styles using ");
69+
writer.write(Integer.toString(header.getChunkSize()));
70+
writer.write(" bytes:");
71+
writer.write("\n");
72+
int size = stringPool.size();
73+
for (int i = 0; i < size; i++ ) {
74+
StringItem item = stringPool.get(i);
75+
writer.write("String #");
76+
writer.write(Integer.toString(i));
77+
writer.write(": ");
78+
writer.write(item.get());
79+
writer.write("\n");
80+
}
81+
}
82+
83+
@Override
84+
public void writeXmlDocument(String sourcePath, ResXmlDocument xmlDocument) throws IOException {
85+
writeNameValue("source-path", sourcePath);
86+
for (ResXmlNode node : xmlDocument) {
87+
if (node instanceof ResXmlElement) {
88+
writeElement((ResXmlElement) node);
89+
} else if (node instanceof ResXmlTextNode) {
90+
writeTextNode(0, (ResXmlTextNode) node);
91+
}
92+
}
93+
Writer writer = getWriter();
94+
writer.flush();
95+
}
96+
private void writeElement(ResXmlElement element) throws IOException {
97+
int indent = element.getDepth() * 2;
98+
99+
int count = element.getNamespaceCount();
100+
for (int i = 0; i < count; i++) {
101+
writeNamespace(indent, element.getNamespaceAt(i));
102+
}
103+
indent = indent + 2;
104+
Writer writer = getWriter();
105+
writeIndent(indent);
106+
writer.write("E: ");
107+
writer.write(element.getName(true));
108+
writer.write(" (line=");
109+
writer.write(Integer.toString(element.getLineNumber()));
110+
writer.write(")");
111+
writer.write("\n");
112+
113+
Iterator<ResXmlAttribute> attributes = element.getAttributes();
114+
while (attributes.hasNext()) {
115+
writeAttribute(indent, attributes.next());
116+
}
117+
flush();
118+
Iterator<ResXmlNode> iterator = element.iterator();
119+
while (iterator.hasNext()) {
120+
ResXmlNode node = iterator.next();
121+
if (node instanceof ResXmlElement) {
122+
writeElement((ResXmlElement) node);
123+
} else if (node instanceof ResXmlTextNode) {
124+
writeTextNode(indent, (ResXmlTextNode) node);
125+
}
126+
}
127+
}
128+
private void writeTextNode(int indent, ResXmlTextNode textNode) throws IOException {
129+
Writer writer = getWriter();
130+
writeIndent(indent + 2);
131+
writer.write("T: \"");
132+
writer.write(textNode.getText());
133+
writer.write("\"");
134+
writer.write("\n");
135+
}
136+
private void writeNamespace(int indent, ResXmlNamespace namespace) throws IOException {
137+
Writer writer = getWriter();
138+
writeIndent(indent);
139+
writer.write("N: ");
140+
writer.write(namespace.getPrefix());
141+
writer.write("=");
142+
writer.write(namespace.getUri());
143+
writer.write("\n");
144+
}
145+
private void writeAttribute(int indent, ResXmlAttribute attribute) throws IOException {
146+
Writer writer = getWriter();
147+
writeIndent(indent + 2);
148+
writer.write("A: ");
149+
Namespace namespace = attribute.getNamespace();
150+
if (namespace != null) {
151+
writer.write(namespace.getPrefix());
152+
writer.append(':');
153+
}
154+
writer.write(attribute.getName());
155+
int id = attribute.getNameId();
156+
if (id != 0) {
157+
writer.append('(');
158+
writer.write(HexUtil.toHex8(id));
159+
writer.append(')');
160+
}
161+
writer.append('=');
162+
ValueType valueType = attribute.getValueType();
163+
if (valueType == ValueType.STRING) {
164+
writer.append('"');
165+
writer.write(attribute.getDataAsPoolString().getXml());
166+
writer.append('"');
167+
writer.write(" (Raw: \"");
168+
writer.write(attribute.getValueString());
169+
writer.write("\")");
170+
} else if (valueType == ValueType.BOOLEAN) {
171+
writer.append('"');
172+
writer.write(attribute.getValueAsBoolean() ? "true" : "false");
173+
writer.append('"');
174+
} else {
175+
writer.write("(type ");
176+
writer.write(HexUtil.toHex(valueType.getByte() & 0xff, 1));
177+
writer.write(")");
178+
writer.write(HexUtil.toHex(attribute.getData(), 1));
179+
}
180+
writer.write("\n");
181+
}
182+
47183
@Override
48184
public void writeCertificates(List<CertificateBlock> certificateList, boolean base64) throws IOException {
49185
Writer writer = getWriter();
@@ -296,6 +432,9 @@ public void flush() throws IOException {
296432
}
297433

298434

435+
private void writeIndent(int amount) throws IOException {
436+
writeSpaces(getWriter(), amount);
437+
}
299438
private void writeWithTab(Writer writer, String tab, String value) throws IOException {
300439
String[] splits = StringsUtil.split(value, '\n');
301440
for(String line : splits){

0 commit comments

Comments
 (0)