* Update export of gradle project * Fix hardcoded index * Add versionCode and versionName to the export template
This commit is contained in:
@@ -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\""));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user