Skip to content

Commit 68f51ff

Browse files
committed
Feat: Build fused modules manifest attribute REAndroid/APKEditor#186
1 parent ceb1491 commit 68f51ff

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

src/main/java/com/reandroid/apk/ApkModule.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import com.reandroid.arsc.value.Entry;
4545
import com.reandroid.arsc.value.ResConfig;
4646
import com.reandroid.identifiers.PackageIdentifier;
47+
import com.reandroid.utils.StringsUtil;
4748
import com.reandroid.utils.collection.ArrayCollection;
4849
import com.reandroid.utils.collection.CollectionUtil;
4950
import com.reandroid.xml.XMLDocument;
@@ -1228,6 +1229,29 @@ public void merge(ApkModule module, boolean force) throws IOException {
12281229
mergeTable(module);
12291230
mergeFiles(module);
12301231
getUncompressedFiles().merge(module.getUncompressedFiles());
1232+
mergeFusedModules(module);
1233+
}
1234+
private void mergeFusedModules(ApkModule module) {
1235+
if (!this.hasAndroidManifest() || !module.hasAndroidManifest()) {
1236+
return;
1237+
}
1238+
AndroidManifestBlock baseManifest = this.getAndroidManifest();
1239+
AndroidManifestBlock manifest = module.getAndroidManifest();
1240+
if (!manifest.isFusingInclude()) {
1241+
return;
1242+
}
1243+
logMessage("Merging fused module: " + module.getModuleName());
1244+
String[] fusedModules = manifest.getFusedModules();
1245+
if (fusedModules != null && fusedModules.length != 0) {
1246+
baseManifest.addFusedModules(fusedModules);
1247+
logMessage("Fused modules added [" +
1248+
StringsUtil.join(fusedModules, ',') + "]");
1249+
}
1250+
String split = manifest.getSplit();
1251+
if (split != null) {
1252+
baseManifest.addFusedModules(split);
1253+
logMessage("Added as fused module <" + split + ">");
1254+
}
12311255
}
12321256
private void validateMerge(ApkModule apkModule, boolean force) throws IOException{
12331257
if (!hasTableBlock()) {

src/main/java/com/reandroid/app/AndroidManifest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,7 @@ default void setCompileSdk(AndroidApiLevel apiLevel){
154154
String TAG_uses_sdk = ObjectsUtil.of("uses-sdk");
155155

156156
String VALUE_android_intent_action_MAIN = ObjectsUtil.of("android.intent.action.MAIN");
157+
String VALUE_com_android_dynamic_apk_fused_modules = ObjectsUtil.of("com.android.dynamic.apk.fused.modules");
157158

158159
String FILE_NAME = ObjectsUtil.of("AndroidManifest.xml");
159160
String FILE_NAME_BIN = ObjectsUtil.of("AndroidManifest.xml.bin");

src/main/java/com/reandroid/arsc/chunk/xml/AndroidManifestBlock.java

Lines changed: 83 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,18 @@
2323
import com.reandroid.arsc.model.ResourceEntry;
2424
import com.reandroid.arsc.value.ValueType;
2525
import com.reandroid.utils.ObjectsUtil;
26-
import com.reandroid.utils.collection.*;
26+
import com.reandroid.utils.StringsUtil;
27+
import com.reandroid.utils.collection.ArrayCollection;
28+
import com.reandroid.utils.collection.CollectionUtil;
29+
import com.reandroid.utils.collection.CombiningIterator;
30+
import com.reandroid.utils.collection.ComputeIterator;
2731

2832
import java.io.File;
2933
import java.io.IOException;
3034
import java.io.InputStream;
3135
import java.util.Iterator;
3236
import java.util.List;
37+
import java.util.function.Predicate;
3338

3439
@SuppressWarnings("unused")
3540
public class AndroidManifestBlock extends ResXmlDocument implements AndroidManifest {
@@ -101,6 +106,78 @@ public void setSplit(String split, boolean forceCreate) {
101106
}
102107
attribute.setValueAsString(split);
103108
}
109+
110+
/**
111+
* Returns "include" value from split manifest
112+
*
113+
* e.g.
114+
* <dist:module type="asset-pack">
115+
* <dist:fusing include="true" />
116+
* </dist:module>
117+
*/
118+
public boolean isFusingInclude() {
119+
ResXmlElement manifestElement = getManifestElement();
120+
if (manifestElement != null) {
121+
Iterator<ResXmlElement> modules = manifestElement.getElements("module");
122+
while (modules.hasNext()) {
123+
Iterator<ResXmlElement> iterator = modules.next().getElements("fusing");
124+
while (iterator.hasNext()) {
125+
ResXmlAttribute attribute = iterator.next()
126+
.searchAttributeByName("include");
127+
if (attribute != null && attribute.getValueType() == ValueType.BOOLEAN) {
128+
return attribute.getValueAsBoolean();
129+
}
130+
}
131+
}
132+
}
133+
return false;
134+
}
135+
public String[] getFusedModules() {
136+
ResXmlElement manifestElement = getManifestElement();
137+
if (manifestElement != null) {
138+
ResXmlElement metaData = CollectionUtil.getFirst(
139+
manifestElement.getElements(PREDICATE_FUSED_MODULES));
140+
if (metaData != null) {
141+
ResXmlAttribute attribute = metaData.searchAttributeByResourceId(ID_value);
142+
if (attribute != null && attribute.getValueType() == ValueType.STRING) {
143+
return StringsUtil.split(attribute.getValueAsString(), ',');
144+
}
145+
}
146+
}
147+
return null;
148+
}
149+
public void addFusedModules(String ... names) {
150+
if (names == null || names.length == 0) {
151+
return;
152+
}
153+
ResXmlElement manifestElement = getOrCreateManifestElement();
154+
ResXmlElement metaData = CollectionUtil.getFirst(
155+
manifestElement.getElements(PREDICATE_FUSED_MODULES));
156+
if (metaData == null) {
157+
metaData = manifestElement.newElement(TAG_meta_data);
158+
metaData.getOrCreateAndroidAttribute(NAME_name, ID_name)
159+
.setValueAsString(VALUE_com_android_dynamic_apk_fused_modules);
160+
}
161+
ResXmlAttribute attribute = metaData.getOrCreateAndroidAttribute(NAME_value, ID_value);
162+
ArrayCollection<String> nameList = new ArrayCollection<>();
163+
String value = attribute.getValueAsString();
164+
if (value != null) {
165+
nameList.addAll(StringsUtil.split(value, ','));
166+
}
167+
for (String name : names) {
168+
if (!StringsUtil.isEmpty(name) && !nameList.contains(name)) {
169+
nameList.add(name);
170+
}
171+
}
172+
attribute.setValueAsString(StringsUtil.join(nameList, ','));
173+
}
174+
public boolean clearFusedModules() {
175+
ResXmlElement manifestElement = getManifestElement();
176+
if (manifestElement != null) {
177+
return manifestElement.removeElementsIf(PREDICATE_FUSED_MODULES);
178+
}
179+
return false;
180+
}
104181
// TODO: find a better way
105182
public int guessCurrentPackageId() {
106183
if (mGuessedPackageId == 0) {
@@ -700,4 +777,9 @@ public static AndroidManifestBlock empty() {
700777
manifestBlock.getOrCreateElement(EMPTY_MANIFEST_TAG);
701778
return manifestBlock;
702779
}
780+
781+
public static final Predicate<ResXmlElement> PREDICATE_FUSED_MODULES = element ->
782+
element.equalsName(TAG_meta_data) &&
783+
VALUE_com_android_dynamic_apk_fused_modules.equals(
784+
getAndroidNameValue(element));
703785
}

0 commit comments

Comments
 (0)