From 63fc7e05b6310f70d9d146eb134104b9930de129 Mon Sep 17 00:00:00 2001 From: Mino Date: Mon, 31 Jul 2023 18:50:47 +0100 Subject: [PATCH] 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 --- .../jadx/core/export/ExportGradleProject.java | 84 +------ .../jadx/core/export/ExportGradleTask.java | 22 +- .../utils/android/AndroidManifestParser.java | 230 ++++++++++++++++++ .../jadx/core/utils/android/AppAttribute.java | 10 + .../android}/ApplicationParams.java | 17 +- .../src/main/java/jadx/gui/ui/MainWindow.java | 20 +- .../jadx/gui/utils/AndroidManifestParser.java | 139 ----------- 7 files changed, 298 insertions(+), 224 deletions(-) create mode 100644 jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java create mode 100644 jadx-core/src/main/java/jadx/core/utils/android/AppAttribute.java rename jadx-core/src/main/java/jadx/core/{export => utils/android}/ApplicationParams.java (68%) delete mode 100644 jadx-gui/src/main/java/jadx/gui/utils/AndroidManifestParser.java diff --git a/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java b/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java index c04c63f9a..345a8cbba 100644 --- a/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java +++ b/jadx-core/src/main/java/jadx/core/export/ExportGradleProject.java @@ -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(); } } diff --git a/jadx-core/src/main/java/jadx/core/export/ExportGradleTask.java b/jadx-core/src/main/java/jadx/core/export/ExportGradleTask.java index 1ff62e753..e31753e23 100644 --- a/jadx-core/src/main/java/jadx/core/export/ExportGradleTask.java +++ b/jadx-core/src/main/java/jadx/core/export/ExportGradleTask.java @@ -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 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); diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java b/jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java new file mode 100644 index 000000000..770fe53fa --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/utils/android/AndroidManifestParser.java @@ -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 parseAttrs; + + public AndroidManifestParser(ResourceFile androidManifestRes, EnumSet parseAttrs) { + this(androidManifestRes, null, parseAttrs); + } + + public AndroidManifestParser(ResourceFile androidManifestRes, ResContainer appStrings, EnumSet 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 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); + } +} diff --git a/jadx-core/src/main/java/jadx/core/utils/android/AppAttribute.java b/jadx-core/src/main/java/jadx/core/utils/android/AppAttribute.java new file mode 100644 index 000000000..0fc9d7938 --- /dev/null +++ b/jadx-core/src/main/java/jadx/core/utils/android/AppAttribute.java @@ -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, +} diff --git a/jadx-core/src/main/java/jadx/core/export/ApplicationParams.java b/jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java similarity index 68% rename from jadx-core/src/main/java/jadx/core/export/ApplicationParams.java rename to jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java index c78628351..8c904ffca 100644 --- a/jadx-core/src/main/java/jadx/core/export/ApplicationParams.java +++ b/jadx-core/src/main/java/jadx/core/utils/android/ApplicationParams.java @@ -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); + } } diff --git a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java index a30b76b3c..216d7c5d1 100644 --- a/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java +++ b/jadx-gui/src/main/java/jadx/gui/ui/MainWindow.java @@ -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); diff --git a/jadx-gui/src/main/java/jadx/gui/utils/AndroidManifestParser.java b/jadx-gui/src/main/java/jadx/gui/utils/AndroidManifestParser.java deleted file mode 100644 index 122830136..000000000 --- a/jadx-gui/src/main/java/jadx/gui/utils/AndroidManifestParser.java +++ /dev/null @@ -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 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 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; - } -}