Skip to content

Commit f8eec50

Browse files
committed
Simplify manifest using XMLPath
1 parent 75dd6f8 commit f8eec50

3 files changed

Lines changed: 106 additions & 134 deletions

File tree

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

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
package com.reandroid.app;
1717

1818
import com.reandroid.utils.ObjectsUtil;
19+
import com.reandroid.xml.XMLPath;
1920

2021
@SuppressWarnings("unused")
2122
public interface AndroidManifest {
@@ -164,6 +165,12 @@ default void setCompileSdk(AndroidApiLevel apiLevel){
164165
String FILE_NAME_BIN = ObjectsUtil.of("AndroidManifest.xml.bin");
165166
String FILE_NAME_JSON = ObjectsUtil.of("AndroidManifest.xml.json");
166167

168+
XMLPath PATH_MANIFEST = XMLPath.newElement(TAG_manifest);
169+
XMLPath PATH_APPLICATION = PATH_MANIFEST.element(TAG_application);
170+
XMLPath PATH_APPLICATION_META_DATA = PATH_APPLICATION.element(TAG_meta_data);
171+
XMLPath PATH_APPLICATION_META_DATA_NAME = PATH_APPLICATION.element(TAG_meta_data)
172+
.attribute(ID_name);
173+
167174
String EMPTY_MANIFEST_TAG = ObjectsUtil.of("x");
168175

169176
}

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

Lines changed: 99 additions & 131 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@
2626
import com.reandroid.utils.StringsUtil;
2727
import com.reandroid.utils.collection.ArrayCollection;
2828
import com.reandroid.utils.collection.CollectionUtil;
29-
import com.reandroid.utils.collection.CombiningIterator;
3029
import com.reandroid.utils.collection.ComputeIterator;
3130
import com.reandroid.xml.XMLPath;
3231

@@ -53,17 +52,33 @@ public ResXmlElement getOrCreateNamedElement(XMLPath xmlPath, String value) {
5352
element = newChildElement(xmlPath);
5453
element.getOrCreateAndroidAttribute(NAME_name, ID_name)
5554
.setValueAsString(value);
55+
ensureAboveApplication(element);
5656
}
5757
return element;
5858
}
59-
public ResXmlElement getNamedElement(XMLPath xmlPath, String value) {
60-
Iterator<ResXmlElement> iterator = xmlPath.find(this);
61-
while (iterator.hasNext()) {
62-
ResXmlElement element = iterator.next();
63-
if (value.equals(getAndroidNameValue(element))) {
64-
return element;
59+
private void ensureAboveApplication(ResXmlElement element) {
60+
String tag = element.getName();
61+
ResXmlElement parent = element.getParentElement();
62+
if (parent != null && !element.equalsName(TAG_application) && parent.equalsName(TAG_manifest)) {
63+
int i = parent.lastIndexOf(tag);
64+
if (i < 0) {
65+
i = parent.lastIndexOf(TAG_application);
66+
} else if (i == element.getIndex()) {
67+
return;
68+
}
69+
if (i >= 0) {
70+
parent.moveTo(element, i);
6571
}
6672
}
73+
}
74+
public ResXmlElement getNamedElement(XMLPath xmlPath, String value) {
75+
ResXmlAttribute attribute = xmlPath.attribute(ID_name)
76+
.value(value)
77+
.alternateValue(value.startsWith(".") ? fullClassName(value) : relativeClassName(value))
78+
.findFirst(this);
79+
if (attribute != null) {
80+
return attribute.getParentElement();
81+
}
6782
return null;
6883
}
6984
public ApkFile.ApkType guessApkType() {
@@ -129,57 +144,37 @@ public void setSplit(String split, boolean forceCreate) {
129144
}
130145

131146
/**
132-
* Returns "include" value from split manifest
133-
*
147+
* Returns "include" value from split module/fusing
134148
* e.g.
135149
* <dist:module type="asset-pack">
136150
* <dist:fusing include="true" />
137151
* </dist:module>
138152
*/
139153
public boolean isFusingInclude() {
140-
ResXmlElement manifestElement = getManifestElement();
141-
if (manifestElement != null) {
142-
Iterator<ResXmlElement> modules = manifestElement.getElements("module");
143-
while (modules.hasNext()) {
144-
Iterator<ResXmlElement> iterator = modules.next().getElements("fusing");
145-
while (iterator.hasNext()) {
146-
ResXmlAttribute attribute = iterator.next()
147-
.searchAttributeByName("include");
148-
if (attribute != null && attribute.getValueType() == ValueType.BOOLEAN) {
149-
return attribute.getValueAsBoolean();
150-
}
151-
}
152-
}
154+
ResXmlAttribute attribute = PATH_MANIFEST.parse("/module/fusing;include=true")
155+
.findFirst(this);
156+
if (attribute != null && attribute.getValueType() == ValueType.BOOLEAN) {
157+
return attribute.getValueAsBoolean();
153158
}
154159
return false;
155160
}
156161
public String[] getFusedModuleNames() {
157-
ResXmlElement applicationElement = getApplicationElement();
158-
if (applicationElement != null) {
159-
ResXmlElement metaData = CollectionUtil.getFirst(
160-
applicationElement.getElements(PREDICATE_FUSED_MODULES));
161-
if (metaData != null) {
162-
ResXmlAttribute attribute = metaData.searchAttributeByResourceId(ID_value);
163-
if (attribute != null && attribute.getValueType() == ValueType.STRING) {
164-
return StringsUtil.split(attribute.getValueAsString(), ',');
165-
}
166-
}
162+
ResXmlAttribute attribute = PATH_APPLICATION_META_DATA_NAME
163+
.value(VALUE_com_android_dynamic_apk_fused_modules)
164+
.findFirst(this);
165+
if (attribute != null && attribute.getValueType() == ValueType.STRING) {
166+
return StringsUtil.split(attribute.getValueAsString(), ',');
167167
}
168168
return null;
169169
}
170170
public void addFusedModuleNames(String ... names) {
171171
if (names == null || names.length == 0) {
172172
return;
173173
}
174-
ResXmlElement applicationElement = getOrCreateApplicationElement();
175-
ResXmlElement metaData = CollectionUtil.getFirst(
176-
applicationElement.getElements(PREDICATE_FUSED_MODULES));
177-
if (metaData == null) {
178-
metaData = applicationElement.newElement(TAG_meta_data);
179-
metaData.getOrCreateAndroidAttribute(NAME_name, ID_name)
180-
.setValueAsString(VALUE_com_android_dynamic_apk_fused_modules);
181-
}
182-
ResXmlAttribute attribute = metaData.getOrCreateAndroidAttribute(NAME_value, ID_value);
174+
ResXmlAttribute attribute = getOrCreateNamedElement(PATH_APPLICATION_META_DATA,
175+
VALUE_com_android_dynamic_apk_fused_modules)
176+
.getOrCreateAndroidAttribute(NAME_value, ID_value);
177+
183178
ArrayCollection<String> nameList = new ArrayCollection<>();
184179
String value = attribute.getValueAsString();
185180
if (value != null) {
@@ -193,11 +188,9 @@ public void addFusedModuleNames(String ... names) {
193188
attribute.setValueAsString(StringsUtil.join(nameList, ','));
194189
}
195190
public boolean clearFusedModuleNames() {
196-
ResXmlElement applicationElement = getApplicationElement();
197-
if (applicationElement != null) {
198-
return applicationElement.removeElementsIf(PREDICATE_FUSED_MODULES);
199-
}
200-
return false;
191+
return removeElements(PATH_APPLICATION_META_DATA_NAME
192+
.attribute(ID_name)
193+
.value(VALUE_com_android_dynamic_apk_fused_modules));
201194
}
202195
// TODO: find a better way
203196
public int guessCurrentPackageId() {
@@ -343,39 +336,39 @@ public void setDebuggable(boolean debuggable) {
343336
}
344337
}
345338
public ResXmlElement getMainActivity() {
346-
Iterator<ResXmlElement> iterator = getActivities(true);
347-
while (iterator.hasNext()) {
348-
ResXmlElement activity = iterator.next();
349-
Iterator<ResXmlElement> actions = activity
350-
.getElementsWithChild(TAG_intent_filter, TAG_action);
351-
while (actions.hasNext()) {
352-
ResXmlElement action = actions.next();
353-
ResXmlAttribute attribute = action.searchAttributeByResourceId(ID_name);
354-
if (attribute == null) {
355-
continue;
356-
}
357-
if (VALUE_android_intent_action_MAIN.equals(attribute.getValueAsString())) {
358-
return activity;
359-
}
360-
}
339+
ResXmlElement element = getNamedElement(
340+
PATH_APPLICATION.element(TAG_activity).alternate(TAG_activity_alias)
341+
.element(TAG_intent_filter)
342+
.element(TAG_action),
343+
VALUE_android_intent_action_MAIN);
344+
if (element != null) {
345+
return element.getParentElement().getParentElement();
361346
}
362347
return null;
363348
}
364349
public ResXmlElement getOrCreateMainActivity(String name) {
365350
ResXmlElement activity = getMainActivity();
366351
if (activity == null) {
367-
ResXmlElement application = getOrCreateApplicationElement();
368-
activity = application.newElementAt(0, TAG_activity);
369-
ResXmlElement intentFilter = activity.newElement(TAG_intent_filter);
370-
ResXmlElement action = intentFilter.newElement(TAG_action);
371-
ResXmlAttribute attribute = action.getOrCreateAndroidAttribute(NAME_name, ID_name);
372-
attribute.setValueAsString(VALUE_android_intent_action_MAIN);
373-
ResXmlElement category = intentFilter.newElement(TAG_category);
374-
attribute = category.getOrCreateAndroidAttribute(NAME_name, ID_name);
375-
attribute.setValueAsString("android.intent.category.DEFAULT");
376-
category = intentFilter.newElement(TAG_category);
377-
attribute = category.getOrCreateAndroidAttribute(NAME_name, ID_name);
378-
attribute.setValueAsString("android.intent.category.LAUNCHER");
352+
ResXmlElement action = newChildElement(
353+
PATH_APPLICATION.element(TAG_activity)
354+
.element(TAG_intent_filter)
355+
.element(TAG_action));
356+
action.getOrCreateAndroidAttribute(NAME_name, ID_name)
357+
.setValueAsString(VALUE_android_intent_action_MAIN);
358+
359+
ResXmlElement intentFilter = action.getParentElement();
360+
361+
intentFilter.newElement(TAG_category)
362+
.getOrCreateAndroidAttribute(NAME_name, ID_name)
363+
.setValueAsString("android.intent.category.DEFAULT");
364+
intentFilter.newElement(TAG_category)
365+
.getOrCreateAndroidAttribute(NAME_name, ID_name)
366+
.setValueAsString("android.intent.category.LAUNCHER");
367+
368+
activity = intentFilter.getParentElement();
369+
370+
activity.getOrCreateAndroidAttribute(NAME_exported, ID_exported)
371+
.setValueAsBoolean(true);
379372
}
380373
ResXmlAttribute attribute = activity.getOrCreateAndroidAttribute(NAME_name, ID_name);
381374
attribute.setValueAsString(name);
@@ -395,10 +388,10 @@ public ResXmlElement getOrCreateActivity(String name, boolean activityAlias) {
395388
}
396389
public ResXmlElement getActivity(String name, boolean activityAlias) {
397390
name = fullClassName(name);
398-
Iterator<ResXmlElement> iterator = getActivities(true);
391+
Iterator<ResXmlElement> iterator = getActivities(activityAlias);
399392
while(iterator.hasNext()) {
400393
ResXmlElement element = iterator.next();
401-
if (ObjectsUtil.equals(name, getAndroidNameValue(element))) {
394+
if (ObjectsUtil.equals(name, fullClassName(getAndroidNameValue(element)))) {
402395
return element;
403396
}
404397
}
@@ -410,64 +403,27 @@ public List<ResXmlElement> listActivities() {
410403
results.addAll(getActivities(true));
411404
return results;
412405
}
413-
@Deprecated
414-
public List<ResXmlElement> listActivities(boolean includeActivityAlias) {
415-
ArrayCollection<ResXmlElement> results = new ArrayCollection<>();
416-
results.addAll(getActivities(includeActivityAlias));
417-
return results;
418-
}
419406
public Iterator<ResXmlElement> getActivities(boolean includeAlias) {
420-
Iterator<ResXmlElement> iterator = getElementsWithChild(
421-
TAG_manifest,
422-
TAG_application,
423-
TAG_activity);
424-
if (!includeAlias) {
425-
return iterator;
426-
}
427-
return CombiningIterator.two(iterator, getElementsWithChild(
428-
TAG_manifest,
429-
TAG_application,
430-
TAG_activity_alias));
407+
XMLPath xmlPath = PATH_APPLICATION.element(TAG_activity);
408+
if (includeAlias) {
409+
xmlPath = xmlPath.alternate(TAG_activity_alias);
410+
}
411+
return xmlPath.find(this);
431412
}
432413
public List<ResXmlElement> listApplicationElementsByTag(String tag) {
433-
return CollectionUtil.toList(getApplicationElementsByTag(tag));
434-
}
435-
public Iterator<ResXmlElement> getApplicationElementsByTag(String tag) {
436-
return getElementsWithChild(TAG_manifest, TAG_application, tag);
414+
return PATH_APPLICATION.element(tag).list(this);
437415
}
438416
public List<String> getUsesPermissions() {
439-
Iterator<String> iterator = ComputeIterator.of(
440-
getElementsWithChild(TAG_manifest, TAG_uses_permission),
441-
AndroidManifestBlock::getAndroidNameValue
442-
);
443-
return CollectionUtil.toList(iterator);
417+
Iterator<ResXmlAttribute> iterator = PATH_MANIFEST.element(TAG_uses_permission)
418+
.attribute(ID_name)
419+
.find(this);
420+
return CollectionUtil.toList(ComputeIterator.of(iterator, ResXmlAttribute::getValueAsString));
444421
}
445422
public ResXmlElement getUsesPermission(String permissionName) {
446-
Iterator<ResXmlElement> iterator = getElementsWithChild(TAG_manifest, TAG_uses_permission);
447-
while (iterator.hasNext()) {
448-
ResXmlElement element = iterator.next();
449-
if (ObjectsUtil.equals(permissionName, getAndroidNameValue(element))) {
450-
return element;
451-
}
452-
}
453-
return null;
423+
return getNamedElement(PATH_MANIFEST.element(TAG_uses_permission), permissionName);
454424
}
455425
public ResXmlElement addUsesPermission(String permissionName) {
456-
ResXmlElement manifestElement = getManifestElement();
457-
if (manifestElement == null) {
458-
return null;
459-
}
460-
ResXmlElement exist = getUsesPermission(permissionName);
461-
if (exist != null) {
462-
return exist;
463-
}
464-
int i = manifestElement.lastIndexOf(TAG_uses_permission);
465-
i++;
466-
ResXmlElement result = manifestElement.newElement(TAG_uses_permission);
467-
ResXmlAttribute attr = result.getOrCreateAndroidAttribute(AndroidManifest.NAME_name, ID_name);
468-
attr.setValueAsString(permissionName);
469-
manifestElement.moveTo(result, i);
470-
return result;
426+
return getOrCreateNamedElement(PATH_MANIFEST.element(TAG_uses_permission), permissionName);
471427
}
472428
@Override
473429
public String getPackageName() {
@@ -490,13 +446,11 @@ public void setPackageName(String packageName) {
490446
}
491447
@Override
492448
public String getApplicationClassName() {
493-
ResXmlElement applicationElement = getApplicationElement();
494-
if (applicationElement != null) {
495-
ResXmlAttribute attribute = applicationElement
496-
.searchAttributeByResourceId(ID_name);
497-
if (attribute != null) {
498-
return fullClassName(attribute.getValueAsString());
499-
}
449+
ResXmlAttribute attribute = PATH_APPLICATION
450+
.attribute(ID_name)
451+
.findFirst(this);
452+
if (attribute != null) {
453+
return fullClassName(attribute.getValueAsString());
500454
}
501455
return null;
502456
}
@@ -735,6 +689,20 @@ public String fullClassName(String name) {
735689
}
736690
return packageName + name;
737691
}
692+
public String relativeClassName(String name) {
693+
if (name == null || name.length() == 0 || name.charAt(0) == '.') {
694+
return name;
695+
}
696+
String packageName = getPackageName();
697+
if (packageName == null || !name.startsWith(packageName)) {
698+
return name;
699+
}
700+
int i = packageName.length();
701+
if (name.length() == i || name.charAt(i) != '.') {
702+
return name;
703+
}
704+
return name.substring(i);
705+
}
738706
private ResXmlElement getOrCreateManifestElement() {
739707
return getOrCreateElement(AndroidManifest.TAG_manifest);
740708
}

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,9 +146,6 @@ public ResXmlElement newChildElement(XMLPath xmlPath) {
146146
return getOrCreateChildElement(parent)
147147
.newElement(name);
148148
}
149-
if (this instanceof ResXmlDocument) {
150-
return getOrCreateElement(name);
151-
}
152149
return newElement(name);
153150
}
154151
public ResXmlElement getOrCreateChildElement(XMLPath xmlPath) {

0 commit comments

Comments
 (0)