fix: new gradle export (#1095) (PR #1097)

* Update export of gradle project

* Fix hardcoded index

* Add versionCode and versionName to the export template
This commit is contained in:
Fi5t
2021-01-26 21:31:12 +03:00
committed by GitHub
parent ffb2956d90
commit 4217aab933
7 changed files with 211 additions and 39 deletions
@@ -39,6 +39,7 @@ import jadx.core.export.ExportGradleProject;
import jadx.core.utils.Utils;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.xmlgen.BinaryXMLParser;
import jadx.core.xmlgen.ResContainer;
import jadx.core.xmlgen.ResourcesSaver;
/**
@@ -193,7 +194,23 @@ public final class JadxDecompiler implements Closeable {
File sourcesOutDir;
File resOutDir;
if (args.isExportAsGradleProject()) {
ExportGradleProject export = new ExportGradleProject(root, args.getOutDir());
ResourceFile androidManifest = resources.stream()
.filter(resourceFile -> resourceFile.getType() == ResourceType.MANIFEST)
.findFirst()
.orElseThrow(IllegalStateException::new);
ResContainer strings = resources.stream()
.filter(resourceFile -> resourceFile.getType() == ResourceType.ARSC)
.findFirst()
.orElseThrow(IllegalStateException::new)
.loadContent()
.getSubFiles()
.stream()
.filter(resContainer -> resContainer.getFileName().contains("strings.xml"))
.findFirst()
.orElseThrow(IllegalStateException::new);
ExportGradleProject export = new ExportGradleProject(root, args.getOutDir(), androidManifest, strings);
export.init();
sourcesOutDir = export.getSrcOutDir();
resOutDir = export.getResOutDir();
@@ -0,0 +1,39 @@
package jadx.core.export;
public class ApplicationParams {
private final String applicationLabel;
private final Integer minSdkVersion;
private final Integer targetSdkVersion;
private final Integer versionCode;
private final String versionName;
public ApplicationParams(String applicationLabel, Integer minSdkVersion, Integer targetSdkVersion, Integer versionCode,
String versionName) {
this.applicationLabel = applicationLabel;
this.minSdkVersion = minSdkVersion;
this.targetSdkVersion = targetSdkVersion;
this.versionCode = versionCode;
this.versionName = versionName;
}
public String getApplicationName() {
return applicationLabel;
}
public Integer getMinSdkVersion() {
return minSdkVersion;
}
public Integer getTargetSdkVersion() {
return targetSdkVersion;
}
public Integer getVersionCode() {
return versionCode;
}
public String getVersionName() {
return versionName;
}
}
@@ -2,18 +2,28 @@ package jadx.core.export;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
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.core.dex.attributes.AFlag;
import jadx.core.dex.nodes.ClassNode;
import jadx.core.dex.nodes.RootNode;
import jadx.core.utils.exceptions.JadxRuntimeException;
import jadx.core.utils.files.FileUtils;
import jadx.core.xmlgen.ResContainer;
public class ExportGradleProject {
@@ -24,39 +34,62 @@ public class ExportGradleProject {
"BuildConfig"));
private final RootNode root;
private final File outDir;
private final File projectDir;
private final File appDir;
private final File srcOutDir;
private final File resOutDir;
private final ApplicationParams applicationParams;
public ExportGradleProject(RootNode root, File outDir) {
public ExportGradleProject(RootNode root, File projectDir, ResourceFile androidManifest, ResContainer appStrings) {
this.root = root;
this.outDir = outDir;
this.srcOutDir = new File(outDir, "src/main/java");
this.resOutDir = new File(outDir, "src/main");
this.projectDir = projectDir;
this.appDir = new File(projectDir, "app");
this.srcOutDir = new File(appDir, "src/main/java");
this.resOutDir = new File(appDir, "src/main");
this.applicationParams = getApplicationParams(
parseAndroidManifest(androidManifest),
parseAppStrings(appStrings));
}
public void init() {
try {
FileUtils.makeDirs(srcOutDir);
FileUtils.makeDirs(resOutDir);
saveBuildGradle();
saveProjectBuildGradle();
saveApplicationBuildGradle();
saveSettingsGradle();
skipGeneratedClasses();
} catch (Exception e) {
throw new JadxRuntimeException("Gradle export failed", e);
}
}
private void saveBuildGradle() throws IOException {
private void saveProjectBuildGradle() throws IOException {
TemplateFile tmpl = TemplateFile.fromResources("/export/build.gradle.tmpl");
tmpl.save(new File(projectDir, "build.gradle"));
}
private void saveSettingsGradle() throws IOException {
TemplateFile tmpl = TemplateFile.fromResources("/export/settings.gradle.tmpl");
tmpl.add("applicationName", applicationParams.getApplicationName());
tmpl.save(new File(projectDir, "settings.gradle"));
}
private void saveApplicationBuildGradle() throws IOException {
TemplateFile tmpl = TemplateFile.fromResources("/export/app.build.gradle.tmpl");
String appPackage = root.getAppPackage();
if (appPackage == null) {
appPackage = "UNKNOWN";
}
tmpl.add("applicationId", appPackage);
// TODO: load from AndroidManifest.xml
tmpl.add("minSdkVersion", 9);
tmpl.add("targetSdkVersion", 21);
tmpl.save(new File(outDir, "build.gradle"));
tmpl.add("minSdkVersion", applicationParams.getMinSdkVersion());
tmpl.add("targetSdkVersion", applicationParams.getTargetSdkVersion());
tmpl.add("versionCode", applicationParams.getVersionCode());
tmpl.add("versionName", applicationParams.getVersionName());
tmpl.save(new File(appDir, "build.gradle"));
}
private void skipGeneratedClasses() {
@@ -69,6 +102,60 @@ public class ExportGradleProject {
}
}
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"));
Integer targetSdk = Integer.valueOf(usesSdk.getAttribute("android:targetSdkVersion"));
String appName = "UNKNOWN";
String appLabelName = application.getAttribute("android:label").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;
}
}
return new ApplicationParams(appName, minSdk, targetSdk, versionCode, versionName);
}
private Document parseXml(String xmlContent) {
try {
DocumentBuilder builder = DocumentBuilderFactory.newInstance().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);
}
public File getSrcOutDir() {
return srcOutDir;
}
@@ -0,0 +1,38 @@
plugins {
id 'com.android.application'
}
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId '{{applicationId}}'
minSdkVersion {{minSdkVersion}}
targetSdkVersion {{targetSdkVersion}}
versionCode {{versionCode}}
versionName "{{versionName}}"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
lintOptions {
abortOnError false
}
}
dependencies {
// some dependencies
}
@@ -1,35 +1,20 @@
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.5.0'
}
}
apply plugin: 'com.android.application'
repositories {
mavenCentral()
jcenter()
}
android {
compileSdkVersion 23
buildToolsVersion '23.0.1'
defaultConfig {
applicationId '{{applicationId}}'
minSdkVersion {{minSdkVersion}}
targetSdkVersion {{targetSdkVersion}}
versionCode 1
versionName "1.0"
}
lintOptions {
abortOnError false
classpath 'com.android.tools.build:gradle:4.0.0'
}
}
dependencies {
// some dependencies
allprojects {
repositories {
google()
jcenter()
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
@@ -0,0 +1,2 @@
include ':app'
rootProject.name = '{{applicationName}}'
@@ -11,14 +11,18 @@ public class TemplateFileTest {
@Test
public void testBuildGradle() throws Exception {
TemplateFile tmpl = TemplateFile.fromResources("/export/build.gradle.tmpl");
TemplateFile tmpl = TemplateFile.fromResources("/export/app.build.gradle.tmpl");
tmpl.add("applicationId", "SOME_ID");
tmpl.add("minSdkVersion", 1);
tmpl.add("targetSdkVersion", 2);
tmpl.add("versionCode", 3);
tmpl.add("versionName", "1.2.3");
String res = tmpl.build();
System.out.println(res);
assertThat(res, containsString("applicationId 'SOME_ID'"));
assertThat(res, containsString("targetSdkVersion 2"));
assertThat(res, containsString("versionCode 3"));
assertThat(res, containsString("versionName \"1.2.3\""));
}
}