refactor: reuse the same parser in main activity action and gradle export feature (PR #1971)
* internal: reuse the same parser in Main Activity action and export gradle project * removed unnecessary logs * fixed code formatting issues * removed no longer used methods * optimize imports * fix exception when app name isn't found * use EnumSet instead of int for parse flags * moved ApplicationParams class under android utils package * moved attributes parsing to a seperate method * fallback to any strings.xml if default one is not found
This commit is contained in:
@@ -2,23 +2,18 @@ package jadx.core.export;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.android.AndroidManifestParser;
|
||||
import jadx.core.utils.android.AppAttribute;
|
||||
import jadx.core.utils.android.ApplicationParams;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.XmlSecurity;
|
||||
|
||||
public class ExportGradleProject {
|
||||
private static final Pattern ILLEGAL_GRADLE_CHARS = Pattern.compile("[/\\\\:>\"?*|]");
|
||||
@@ -32,9 +27,7 @@ public class ExportGradleProject {
|
||||
this.root = root;
|
||||
this.projectDir = projectDir;
|
||||
this.appDir = new File(projectDir, "app");
|
||||
this.applicationParams = getApplicationParams(
|
||||
parseAndroidManifest(androidManifest),
|
||||
parseAppStrings(appStrings));
|
||||
this.applicationParams = getApplicationParams(androidManifest, appStrings);
|
||||
}
|
||||
|
||||
public void generateGradleFiles() {
|
||||
@@ -96,64 +89,13 @@ public class ExportGradleProject {
|
||||
tmpl.add("additionalOptions", sb.toString());
|
||||
}
|
||||
|
||||
private ApplicationParams getApplicationParams(Document androidManifest, Document appStrings) {
|
||||
Element manifest = (Element) androidManifest.getElementsByTagName("manifest").item(0);
|
||||
Element usesSdk = (Element) androidManifest.getElementsByTagName("uses-sdk").item(0);
|
||||
Element application = (Element) androidManifest.getElementsByTagName("application").item(0);
|
||||
|
||||
Integer versionCode = Integer.valueOf(manifest.getAttribute("android:versionCode"));
|
||||
String versionName = manifest.getAttribute("android:versionName");
|
||||
Integer minSdk = Integer.valueOf(usesSdk.getAttribute("android:minSdkVersion"));
|
||||
String stringTargetSdk = usesSdk.getAttribute("android:targetSdkVersion");
|
||||
Integer targetSdk = stringTargetSdk.isEmpty() ? minSdk : Integer.valueOf(stringTargetSdk);
|
||||
String appName = "UNKNOWN";
|
||||
|
||||
if (application.hasAttribute("android:label")) {
|
||||
String appLabelName = application.getAttribute("android:label");
|
||||
if (appLabelName.startsWith("@string")) {
|
||||
appLabelName = appLabelName.split("/")[1];
|
||||
NodeList strings = appStrings.getElementsByTagName("string");
|
||||
|
||||
for (int i = 0; i < strings.getLength(); i++) {
|
||||
String stringName = strings.item(i)
|
||||
.getAttributes()
|
||||
.getNamedItem("name")
|
||||
.getNodeValue();
|
||||
|
||||
if (stringName.equals(appLabelName)) {
|
||||
appName = strings.item(i).getTextContent();
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
appName = appLabelName;
|
||||
}
|
||||
}
|
||||
return new ApplicationParams(appName, minSdk, targetSdk, versionCode, versionName);
|
||||
}
|
||||
|
||||
private Document parseXml(String xmlContent) {
|
||||
try {
|
||||
DocumentBuilder builder = XmlSecurity.getSecureDbf().newDocumentBuilder();
|
||||
Document document = builder.parse(new InputSource(new StringReader(xmlContent)));
|
||||
|
||||
document.getDocumentElement().normalize();
|
||||
|
||||
return document;
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Can not parse xml content", e);
|
||||
}
|
||||
}
|
||||
|
||||
private Document parseAppStrings(ResContainer appStrings) {
|
||||
String content = appStrings.getText().getCodeStr();
|
||||
|
||||
return parseXml(content);
|
||||
}
|
||||
|
||||
private Document parseAndroidManifest(ResourceFile androidManifest) {
|
||||
String content = androidManifest.loadContent().getText().getCodeStr();
|
||||
|
||||
return parseXml(content);
|
||||
private ApplicationParams getApplicationParams(ResourceFile androidManifest, ResContainer appStrings) {
|
||||
AndroidManifestParser parser = new AndroidManifestParser(androidManifest, appStrings, EnumSet.of(
|
||||
AppAttribute.APPLICATION_LABEL,
|
||||
AppAttribute.MIN_SDK_VERSION,
|
||||
AppAttribute.TARGET_SDK_VERSION,
|
||||
AppAttribute.VERSION_CODE,
|
||||
AppAttribute.VERSION_NAME));
|
||||
return parser.parse();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.TaskBarrier;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.android.AndroidManifestParser;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
@@ -35,21 +36,26 @@ public class ExportGradleTask implements Runnable {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
ResourceFile androidManifest = resources.stream()
|
||||
.filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST)
|
||||
.findFirst()
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
ResourceFile androidManifest = AndroidManifestParser.getAndroidManifest(resources);
|
||||
if (androidManifest == null) {
|
||||
throw new IllegalStateException("Could not find AndroidManifest.xml");
|
||||
}
|
||||
|
||||
ResContainer strings = resources.stream()
|
||||
List<ResContainer> resContainers = resources.stream()
|
||||
.filter(resourceFile -> resourceFile.getType() == ResourceType.ARSC)
|
||||
.findFirst()
|
||||
.orElseThrow(IllegalStateException::new)
|
||||
.loadContent()
|
||||
.getSubFiles()
|
||||
.getSubFiles();
|
||||
|
||||
ResContainer strings = resContainers
|
||||
.stream()
|
||||
.filter(resContainer -> resContainer.getFileName().contains("strings.xml"))
|
||||
.filter(resContainer -> resContainer.getName().contains("values/strings.xml"))
|
||||
.findFirst()
|
||||
.orElseThrow(IllegalStateException::new);
|
||||
.orElseGet(() -> resContainers.stream()
|
||||
.filter(resContainer -> resContainer.getFileName().contains("strings.xml"))
|
||||
.findFirst()
|
||||
.orElseThrow(IllegalStateException::new));
|
||||
|
||||
ExportGradleProject export = new ExportGradleProject(root, projectDir, androidManifest, strings);
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
package jadx.core.utils.android;
|
||||
|
||||
import java.io.StringReader;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.XmlSecurity;
|
||||
|
||||
public class AndroidManifestParser {
|
||||
private static final Logger LOG = LoggerFactory.getLogger(AndroidManifestParser.class);
|
||||
|
||||
private final Document androidManifest;
|
||||
private final Document appStrings;
|
||||
private final EnumSet<AppAttribute> parseAttrs;
|
||||
|
||||
public AndroidManifestParser(ResourceFile androidManifestRes, EnumSet<AppAttribute> parseAttrs) {
|
||||
this(androidManifestRes, null, parseAttrs);
|
||||
}
|
||||
|
||||
public AndroidManifestParser(ResourceFile androidManifestRes, ResContainer appStrings, EnumSet<AppAttribute> parseAttrs) {
|
||||
this.parseAttrs = parseAttrs;
|
||||
|
||||
this.androidManifest = parseAndroidManifest(androidManifestRes);
|
||||
this.appStrings = parseAppStrings(appStrings);
|
||||
|
||||
validateAttrs();
|
||||
}
|
||||
|
||||
public boolean isManifestFound() {
|
||||
return androidManifest != null;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ResourceFile getAndroidManifest(List<ResourceFile> resources) {
|
||||
return resources.stream()
|
||||
.filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public ApplicationParams parse() {
|
||||
if (!isManifestFound()) {
|
||||
throw new JadxRuntimeException("AndroidManifest.xml is missing");
|
||||
}
|
||||
|
||||
return parseAttributes();
|
||||
}
|
||||
|
||||
private void validateAttrs() {
|
||||
if (parseAttrs.contains(AppAttribute.APPLICATION_LABEL) && appStrings == null) {
|
||||
throw new IllegalArgumentException("APPLICATION_LABEL attribute requires non null appStrings");
|
||||
}
|
||||
}
|
||||
|
||||
private ApplicationParams parseAttributes() {
|
||||
String applicationLabel = null;
|
||||
Integer minSdkVersion = null;
|
||||
Integer targetSdkVersion = null;
|
||||
Integer versionCode = null;
|
||||
String versionName = null;
|
||||
String mainActivity = null;
|
||||
|
||||
Element manifest = (Element) androidManifest.getElementsByTagName("manifest").item(0);
|
||||
Element usesSdk = (Element) androidManifest.getElementsByTagName("uses-sdk").item(0);
|
||||
|
||||
if (parseAttrs.contains(AppAttribute.APPLICATION_LABEL)) {
|
||||
applicationLabel = getApplicationLabel();
|
||||
}
|
||||
if (parseAttrs.contains(AppAttribute.MIN_SDK_VERSION)) {
|
||||
minSdkVersion = Integer.valueOf(usesSdk.getAttribute("android:minSdkVersion"));
|
||||
}
|
||||
if (parseAttrs.contains(AppAttribute.TARGET_SDK_VERSION)) {
|
||||
String stringTargetSdk = usesSdk.getAttribute("android:targetSdkVersion");
|
||||
if (!stringTargetSdk.isEmpty()) {
|
||||
targetSdkVersion = Integer.valueOf(stringTargetSdk);
|
||||
} else {
|
||||
if (minSdkVersion == null) {
|
||||
minSdkVersion = Integer.valueOf(usesSdk.getAttribute("android:minSdkVersion"));
|
||||
}
|
||||
targetSdkVersion = minSdkVersion;
|
||||
}
|
||||
}
|
||||
if (parseAttrs.contains(AppAttribute.VERSION_CODE)) {
|
||||
versionCode = Integer.valueOf(manifest.getAttribute("android:versionCode"));
|
||||
}
|
||||
if (parseAttrs.contains(AppAttribute.VERSION_NAME)) {
|
||||
versionName = manifest.getAttribute("android:versionName");
|
||||
}
|
||||
if (parseAttrs.contains(AppAttribute.MAIN_ACTIVITY)) {
|
||||
mainActivity = getMainActivityName();
|
||||
}
|
||||
|
||||
return new ApplicationParams(applicationLabel, minSdkVersion, targetSdkVersion, versionCode,
|
||||
versionName, mainActivity);
|
||||
}
|
||||
|
||||
private String getApplicationLabel() {
|
||||
Element application = (Element) androidManifest.getElementsByTagName("application").item(0);
|
||||
if (application.hasAttribute("android:label")) {
|
||||
String appLabelName = application.getAttribute("android:label");
|
||||
if (appLabelName.startsWith("@string")) {
|
||||
appLabelName = appLabelName.split("/")[1];
|
||||
NodeList strings = appStrings.getElementsByTagName("string");
|
||||
|
||||
for (int i = 0; i < strings.getLength(); i++) {
|
||||
String stringName = strings.item(i)
|
||||
.getAttributes()
|
||||
.getNamedItem("name")
|
||||
.getNodeValue();
|
||||
|
||||
if (stringName.equals(appLabelName)) {
|
||||
return strings.item(i).getTextContent();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return appLabelName;
|
||||
}
|
||||
}
|
||||
|
||||
return "UNKNOWN";
|
||||
}
|
||||
|
||||
private String getMainActivityName() {
|
||||
String mainActivityName = getMainActivityNameThroughActivityTag();
|
||||
if (mainActivityName == null) {
|
||||
mainActivityName = getMainActivityNameThroughActivityAliasTag();
|
||||
}
|
||||
return mainActivityName;
|
||||
}
|
||||
|
||||
private String getMainActivityNameThroughActivityAliasTag() {
|
||||
NodeList activityAliasNodes = androidManifest.getElementsByTagName("activity-alias");
|
||||
for (int i = 0; i < activityAliasNodes.getLength(); i++) {
|
||||
Element activityElement = (Element) activityAliasNodes.item(i);
|
||||
if (isMainActivityElement(activityElement)) {
|
||||
return activityElement.getAttribute("android:targetActivity");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getMainActivityNameThroughActivityTag() {
|
||||
NodeList activityNodes = androidManifest.getElementsByTagName("activity");
|
||||
for (int i = 0; i < activityNodes.getLength(); i++) {
|
||||
Element activityElement = (Element) activityNodes.item(i);
|
||||
if (isMainActivityElement(activityElement)) {
|
||||
return activityElement.getAttribute("android:name");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isMainActivityElement(Element element) {
|
||||
NodeList intentFilterNodes = element.getElementsByTagName("intent-filter");
|
||||
for (int j = 0; j < intentFilterNodes.getLength(); j++) {
|
||||
Element intentFilterElement = (Element) intentFilterNodes.item(j);
|
||||
NodeList actionNodes = intentFilterElement.getElementsByTagName("action");
|
||||
NodeList categoryNodes = intentFilterElement.getElementsByTagName("category");
|
||||
|
||||
boolean isMainAction = false;
|
||||
boolean isLauncherCategory = false;
|
||||
|
||||
for (int k = 0; k < actionNodes.getLength(); k++) {
|
||||
Element actionElement = (Element) actionNodes.item(k);
|
||||
String actionName = actionElement.getAttribute("android:name");
|
||||
if ("android.intent.action.MAIN".equals(actionName)) {
|
||||
isMainAction = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < categoryNodes.getLength(); k++) {
|
||||
Element categoryElement = (Element) categoryNodes.item(k);
|
||||
String categoryName = categoryElement.getAttribute("android:name");
|
||||
if ("android.intent.category.LAUNCHER".equals(categoryName)) {
|
||||
isLauncherCategory = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMainAction && isLauncherCategory) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Document parseXml(String xmlContent) {
|
||||
try {
|
||||
DocumentBuilder builder = XmlSecurity.getSecureDbf().newDocumentBuilder();
|
||||
Document document = builder.parse(new InputSource(new StringReader(xmlContent)));
|
||||
|
||||
document.getDocumentElement().normalize();
|
||||
|
||||
return document;
|
||||
} catch (Exception e) {
|
||||
throw new JadxRuntimeException("Can not parse xml content", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static Document parseAppStrings(ResContainer appStrings) {
|
||||
if (appStrings == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String content = appStrings.getText().getCodeStr();
|
||||
|
||||
return parseXml(content);
|
||||
}
|
||||
|
||||
private static Document parseAndroidManifest(ResourceFile androidManifest) {
|
||||
String content = androidManifest.loadContent().getText().getCodeStr();
|
||||
|
||||
return parseXml(content);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package jadx.core.utils.android;
|
||||
|
||||
public enum AppAttribute {
|
||||
APPLICATION_LABEL,
|
||||
MIN_SDK_VERSION,
|
||||
TARGET_SDK_VERSION,
|
||||
VERSION_CODE,
|
||||
VERSION_NAME,
|
||||
MAIN_ACTIVITY,
|
||||
}
|
||||
+15
-2
@@ -1,4 +1,7 @@
|
||||
package jadx.core.export;
|
||||
package jadx.core.utils.android;
|
||||
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JavaClass;
|
||||
|
||||
public class ApplicationParams {
|
||||
|
||||
@@ -7,14 +10,16 @@ public class ApplicationParams {
|
||||
private final Integer targetSdkVersion;
|
||||
private final Integer versionCode;
|
||||
private final String versionName;
|
||||
private final String mainActivtiy;
|
||||
|
||||
public ApplicationParams(String applicationLabel, Integer minSdkVersion, Integer targetSdkVersion, Integer versionCode,
|
||||
String versionName) {
|
||||
String versionName, String mainActivtiy) {
|
||||
this.applicationLabel = applicationLabel;
|
||||
this.minSdkVersion = minSdkVersion;
|
||||
this.targetSdkVersion = targetSdkVersion;
|
||||
this.versionCode = versionCode;
|
||||
this.versionName = versionName;
|
||||
this.mainActivtiy = mainActivtiy;
|
||||
}
|
||||
|
||||
public String getApplicationName() {
|
||||
@@ -36,4 +41,12 @@ public class ApplicationParams {
|
||||
public String getVersionName() {
|
||||
return versionName;
|
||||
}
|
||||
|
||||
public String getMainActivityName() {
|
||||
return mainActivtiy;
|
||||
}
|
||||
|
||||
public JavaClass getMainActivity(JadxDecompiler decompiler) {
|
||||
return decompiler.searchJavaClassByOrigFullName(mainActivtiy);
|
||||
}
|
||||
}
|
||||
@@ -32,6 +32,7 @@ import java.nio.file.Paths;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Timer;
|
||||
@@ -89,6 +90,9 @@ import jadx.core.export.TemplateFile;
|
||||
import jadx.core.plugins.events.JadxEventsImpl;
|
||||
import jadx.core.utils.ListUtils;
|
||||
import jadx.core.utils.StringUtils;
|
||||
import jadx.core.utils.android.AndroidManifestParser;
|
||||
import jadx.core.utils.android.AppAttribute;
|
||||
import jadx.core.utils.android.ApplicationParams;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.gui.JadxWrapper;
|
||||
@@ -134,7 +138,6 @@ import jadx.gui.ui.treenodes.SummaryNode;
|
||||
import jadx.gui.update.JadxUpdate;
|
||||
import jadx.gui.update.JadxUpdate.IUpdateCallback;
|
||||
import jadx.gui.update.data.Release;
|
||||
import jadx.gui.utils.AndroidManifestParser;
|
||||
import jadx.gui.utils.CacheObject;
|
||||
import jadx.gui.utils.FontUtils;
|
||||
import jadx.gui.utils.ILoadListener;
|
||||
@@ -1033,8 +1036,10 @@ public class MainWindow extends JFrame {
|
||||
Action gotoMainActivityAction = new AbstractAction(NLS.str("menu.goto_main_activity"), ICON_MAIN_ACTIVITY) {
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent ev) {
|
||||
AndroidManifestParser parser = new AndroidManifestParser(getWrapper().getResources());
|
||||
if (!parser.isResourceFound()) {
|
||||
AndroidManifestParser parser = new AndroidManifestParser(
|
||||
AndroidManifestParser.getAndroidManifest(getWrapper().getResources()),
|
||||
EnumSet.of(AppAttribute.MAIN_ACTIVITY));
|
||||
if (!parser.isManifestFound()) {
|
||||
JOptionPane.showMessageDialog(MainWindow.this,
|
||||
NLS.str("error_dialog.not_found", "AndroidManifest.xml"),
|
||||
NLS.str("error_dialog.title"),
|
||||
@@ -1042,7 +1047,14 @@ public class MainWindow extends JFrame {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
JavaClass mainActivityClass = parser.getMainActivity(getWrapper());
|
||||
ApplicationParams results = parser.parse();
|
||||
if (results.getMainActivityName() == null) {
|
||||
throw new JadxRuntimeException("Failed to get main activity name from manifest");
|
||||
}
|
||||
JavaClass mainActivityClass = results.getMainActivity(getWrapper().getDecompiler());
|
||||
if (mainActivityClass == null) {
|
||||
throw new JadxRuntimeException("Failed to find main activity class: " + results.getMainActivityName());
|
||||
}
|
||||
tabbedPane.codeJump(getCacheObject().getNodeCache().makeFrom(mainActivityClass));
|
||||
} catch (Exception e) {
|
||||
LOG.error("Main activity not found", e);
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
package jadx.gui.utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.List;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.InputSource;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import io.reactivex.annotations.Nullable;
|
||||
|
||||
import jadx.api.JavaClass;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.xmlgen.XmlSecurity;
|
||||
import jadx.gui.JadxWrapper;
|
||||
|
||||
public class AndroidManifestParser {
|
||||
private final Document mXmlDocument;
|
||||
|
||||
public AndroidManifestParser(List<ResourceFile> resourceFiles) {
|
||||
mXmlDocument = parseAndroidManifest(getAndroidManifest(resourceFiles));
|
||||
}
|
||||
|
||||
public boolean isResourceFound() {
|
||||
return mXmlDocument != null;
|
||||
}
|
||||
|
||||
public JavaClass getMainActivity(JadxWrapper decompiler) {
|
||||
final String mainActivityName = getMainActivityName();
|
||||
if (mainActivityName == null) {
|
||||
throw new JadxRuntimeException("Failed to get main activity name from manifest");
|
||||
}
|
||||
JavaClass javaClass = decompiler.searchJavaClassByOrigClassName(mainActivityName);
|
||||
if (javaClass == null) {
|
||||
throw new JadxRuntimeException("Failed to find main activity class: " + mainActivityName);
|
||||
}
|
||||
return javaClass;
|
||||
}
|
||||
|
||||
private String getMainActivityName() {
|
||||
String mainActivityName = getMainActivityNameThroughActivityTag();
|
||||
if (mainActivityName == null) {
|
||||
mainActivityName = getMainActivityNameThroughActivityAliasTag();
|
||||
}
|
||||
return mainActivityName;
|
||||
}
|
||||
|
||||
private String getMainActivityNameThroughActivityAliasTag() {
|
||||
NodeList activityAliasNodes = mXmlDocument.getElementsByTagName("activity-alias");
|
||||
for (int i = 0; i < activityAliasNodes.getLength(); i++) {
|
||||
Element activityElement = (Element) activityAliasNodes.item(i);
|
||||
if (isMainActivityElement(activityElement)) {
|
||||
return activityElement.getAttribute("android:targetActivity");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private String getMainActivityNameThroughActivityTag() {
|
||||
NodeList activityNodes = mXmlDocument.getElementsByTagName("activity");
|
||||
for (int i = 0; i < activityNodes.getLength(); i++) {
|
||||
Element activityElement = (Element) activityNodes.item(i);
|
||||
if (isMainActivityElement(activityElement)) {
|
||||
return activityElement.getAttribute("android:name");
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean isMainActivityElement(Element element) {
|
||||
NodeList intentFilterNodes = element.getElementsByTagName("intent-filter");
|
||||
for (int j = 0; j < intentFilterNodes.getLength(); j++) {
|
||||
Element intentFilterElement = (Element) intentFilterNodes.item(j);
|
||||
NodeList actionNodes = intentFilterElement.getElementsByTagName("action");
|
||||
NodeList categoryNodes = intentFilterElement.getElementsByTagName("category");
|
||||
|
||||
boolean isMainAction = false;
|
||||
boolean isLauncherCategory = false;
|
||||
|
||||
for (int k = 0; k < actionNodes.getLength(); k++) {
|
||||
Element actionElement = (Element) actionNodes.item(k);
|
||||
String actionName = actionElement.getAttribute("android:name");
|
||||
if ("android.intent.action.MAIN".equals(actionName)) {
|
||||
isMainAction = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
for (int k = 0; k < categoryNodes.getLength(); k++) {
|
||||
Element categoryElement = (Element) categoryNodes.item(k);
|
||||
String categoryName = categoryElement.getAttribute("android:name");
|
||||
if ("android.intent.category.LAUNCHER".equals(categoryName)) {
|
||||
isLauncherCategory = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (isMainAction && isLauncherCategory) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static ResourceFile getAndroidManifest(List<ResourceFile> resources) {
|
||||
// TODO: taken from ExportGradleTask.java: also use this function there ?
|
||||
return resources.stream()
|
||||
.filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
public static Document parseAndroidManifest(ResourceFile androidManifest) {
|
||||
if (androidManifest == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO: taken from ExportGradleProject.java: also use this function there ?
|
||||
Document androidManifestDocument;
|
||||
try {
|
||||
String xmlContent = androidManifest.loadContent().getText().getCodeStr();
|
||||
DocumentBuilder builder = XmlSecurity.getSecureDbf().newDocumentBuilder();
|
||||
androidManifestDocument = builder.parse(new InputSource(new StringReader(xmlContent)));
|
||||
androidManifestDocument.getDocumentElement().normalize();
|
||||
} catch (ParserConfigurationException | SAXException | IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
return androidManifestDocument;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user