fix(gradle): add legacy support for vector drawables (PR #1879)
This commit is contained in:
@@ -42,7 +42,7 @@ import jadx.core.dex.nodes.MethodNode;
|
||||
import jadx.core.dex.nodes.PackageNode;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.export.ExportGradleProject;
|
||||
import jadx.core.export.ExportGradleTask;
|
||||
import jadx.core.plugins.JadxPluginManager;
|
||||
import jadx.core.utils.DecompilerScheduler;
|
||||
import jadx.core.utils.Utils;
|
||||
@@ -50,7 +50,6 @@ import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.xmlgen.BinaryXMLParser;
|
||||
import jadx.core.xmlgen.ProtoXMLParser;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.ResourcesSaver;
|
||||
|
||||
/**
|
||||
@@ -281,50 +280,42 @@ public final class JadxDecompiler implements Closeable {
|
||||
}
|
||||
File sourcesOutDir;
|
||||
File resOutDir;
|
||||
List<Runnable> tasks = new ArrayList<>();
|
||||
TaskBarrier barrier = new TaskBarrier();
|
||||
if (args.isExportAsGradleProject()) {
|
||||
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();
|
||||
ExportGradleTask gradleExportTask = new ExportGradleTask(resources, root, args.getOutDir(), barrier);
|
||||
gradleExportTask.init();
|
||||
sourcesOutDir = gradleExportTask.getSrcOutDir();
|
||||
resOutDir = gradleExportTask.getResOutDir();
|
||||
tasks.add(gradleExportTask);
|
||||
} else {
|
||||
sourcesOutDir = args.getOutDirSrc();
|
||||
resOutDir = args.getOutDirRes();
|
||||
}
|
||||
List<Runnable> tasks = new ArrayList<>();
|
||||
|
||||
int taskCount = 0;
|
||||
// save resources first because decompilation can hang or fail
|
||||
if (saveResources) {
|
||||
appendResourcesSaveTasks(tasks, resOutDir);
|
||||
taskCount = appendResourcesSaveTasks(tasks, resOutDir, barrier);
|
||||
}
|
||||
if (saveSources) {
|
||||
appendSourcesSave(tasks, sourcesOutDir);
|
||||
taskCount += appendSourcesSave(tasks, sourcesOutDir, barrier);
|
||||
}
|
||||
barrier.setUpBarrier(taskCount);
|
||||
|
||||
return tasks;
|
||||
}
|
||||
|
||||
private void appendResourcesSaveTasks(List<Runnable> tasks, File outDir) {
|
||||
private int appendResourcesSaveTasks(List<Runnable> tasks, File outDir, TaskBarrier barrier) {
|
||||
int numResourceTasks = 0;
|
||||
if (args.isSkipFilesSave()) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
// process AndroidManifest.xml first to load complete resource ids table
|
||||
for (ResourceFile resourceFile : getResources()) {
|
||||
if (resourceFile.getType() == ResourceType.MANIFEST) {
|
||||
new ResourcesSaver(outDir, resourceFile).run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -340,11 +331,14 @@ public final class JadxDecompiler implements Closeable {
|
||||
// ignore resource made from input file
|
||||
continue;
|
||||
}
|
||||
tasks.add(new ResourcesSaver(outDir, resourceFile));
|
||||
tasks.add(new ResourcesSaver(outDir, resourceFile, barrier));
|
||||
numResourceTasks++;
|
||||
}
|
||||
return numResourceTasks;
|
||||
}
|
||||
|
||||
private void appendSourcesSave(List<Runnable> tasks, File outDir) {
|
||||
private int appendSourcesSave(List<Runnable> tasks, File outDir, TaskBarrier barrier) {
|
||||
int numSourceTasks = 0;
|
||||
Predicate<String> classFilter = args.getClassFilter();
|
||||
List<JavaClass> classes = getClasses();
|
||||
List<JavaClass> processQueue = new ArrayList<>(classes.size());
|
||||
@@ -369,17 +363,25 @@ public final class JadxDecompiler implements Closeable {
|
||||
}
|
||||
for (List<JavaClass> decompileBatch : batches) {
|
||||
tasks.add(() -> {
|
||||
for (JavaClass cls : decompileBatch) {
|
||||
try {
|
||||
ClassNode clsNode = cls.getClassNode();
|
||||
ICodeInfo code = clsNode.getCode();
|
||||
SaveCode.save(outDir, clsNode, code);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error saving class: {}", cls, e);
|
||||
try {
|
||||
for (JavaClass cls : decompileBatch) {
|
||||
try {
|
||||
ClassNode clsNode = cls.getClassNode();
|
||||
ICodeInfo code = clsNode.getCode();
|
||||
SaveCode.save(outDir, clsNode, code);
|
||||
} catch (Exception e) {
|
||||
LOG.error("Error saving class: {}", cls, e);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (barrier != null) {
|
||||
barrier.finishTask();
|
||||
}
|
||||
}
|
||||
});
|
||||
numSourceTasks++;
|
||||
}
|
||||
return numSourceTasks;
|
||||
}
|
||||
|
||||
public List<JavaClass> getClasses() {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
package jadx.api;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
public class TaskBarrier {
|
||||
|
||||
private CountDownLatch taskCountDown = null;
|
||||
|
||||
public void setUpBarrier(final int numTasks) {
|
||||
taskCountDown = new CountDownLatch(numTasks);
|
||||
}
|
||||
|
||||
public CountDownLatch getTaskCountDown() {
|
||||
return taskCountDown;
|
||||
}
|
||||
|
||||
public void finishTask() {
|
||||
if (taskCountDown != null) {
|
||||
taskCountDown.countDown();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -46,6 +46,7 @@ import jadx.core.dex.visitors.DepthTraversal;
|
||||
import jadx.core.dex.visitors.IDexTreeVisitor;
|
||||
import jadx.core.dex.visitors.typeinference.TypeCompare;
|
||||
import jadx.core.dex.visitors.typeinference.TypeUpdate;
|
||||
import jadx.core.export.GradleInfoStorage;
|
||||
import jadx.core.utils.CacheStorage;
|
||||
import jadx.core.utils.ErrorsCounter;
|
||||
import jadx.core.utils.PassMerge;
|
||||
@@ -78,6 +79,8 @@ public class RootNode {
|
||||
private final TypeUtils typeUtils;
|
||||
private final AttributeStorage attributes = new AttributeStorage();
|
||||
|
||||
private final GradleInfoStorage gradleInfoStorage = new GradleInfoStorage();
|
||||
|
||||
private final Map<ClassInfo, ClassNode> clsMap = new HashMap<>();
|
||||
private final Map<String, ClassNode> rawClsMap = new HashMap<>();
|
||||
private List<ClassNode> classes = new ArrayList<>();
|
||||
@@ -712,4 +715,8 @@ public class RootNode {
|
||||
public boolean isProto() {
|
||||
return isProto;
|
||||
}
|
||||
|
||||
public GradleInfoStorage getGradleInfoStorage() {
|
||||
return gradleInfoStorage;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package jadx.core.export;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.StringReader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
@@ -15,7 +17,6 @@ import org.xml.sax.InputSource;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.utils.exceptions.JadxRuntimeException;
|
||||
import jadx.core.utils.files.FileUtils;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
import jadx.core.xmlgen.XmlSecurity;
|
||||
|
||||
@@ -25,25 +26,19 @@ public class ExportGradleProject {
|
||||
private final RootNode root;
|
||||
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 projectDir, ResourceFile androidManifest, ResContainer appStrings) {
|
||||
this.root = root;
|
||||
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() {
|
||||
public void generateGradleFiles() {
|
||||
try {
|
||||
FileUtils.makeDirs(srcOutDir);
|
||||
FileUtils.makeDirs(resOutDir);
|
||||
saveProjectBuildGradle();
|
||||
saveApplicationBuildGradle();
|
||||
saveSettingsGradle();
|
||||
@@ -72,14 +67,32 @@ public class ExportGradleProject {
|
||||
appPackage = "UNKNOWN";
|
||||
}
|
||||
|
||||
Integer minSdkVersion = applicationParams.getMinSdkVersion();
|
||||
|
||||
tmpl.add("applicationId", appPackage);
|
||||
tmpl.add("minSdkVersion", applicationParams.getMinSdkVersion());
|
||||
tmpl.add("minSdkVersion", minSdkVersion);
|
||||
tmpl.add("targetSdkVersion", applicationParams.getTargetSdkVersion());
|
||||
tmpl.add("versionCode", applicationParams.getVersionCode());
|
||||
tmpl.add("versionName", applicationParams.getVersionName());
|
||||
|
||||
List<String> additionalOptions = new ArrayList<>();
|
||||
GradleInfoStorage gradleInfo = root.getGradleInfoStorage();
|
||||
if (gradleInfo.isVectorPathData() && minSdkVersion < 21 || gradleInfo.isVectorFillType() && minSdkVersion < 24) {
|
||||
additionalOptions.add("vectorDrawables.useSupportLibrary = true");
|
||||
}
|
||||
genAdditionalAndroidPluginOptions(tmpl, additionalOptions);
|
||||
|
||||
tmpl.save(new File(appDir, "build.gradle"));
|
||||
}
|
||||
|
||||
private void genAdditionalAndroidPluginOptions(TemplateFile tmpl, List<String> additionalOptions) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (String additionalOption : additionalOptions) {
|
||||
sb.append(" ").append(additionalOption).append('\n');
|
||||
}
|
||||
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);
|
||||
@@ -140,12 +153,4 @@ public class ExportGradleProject {
|
||||
|
||||
return parseXml(content);
|
||||
}
|
||||
|
||||
public File getSrcOutDir() {
|
||||
return srcOutDir;
|
||||
}
|
||||
|
||||
public File getResOutDir() {
|
||||
return resOutDir;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
package jadx.core.export;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourceType;
|
||||
import jadx.api.TaskBarrier;
|
||||
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 ExportGradleTask implements Runnable {
|
||||
|
||||
private final List<ResourceFile> resources;
|
||||
|
||||
private final RootNode root;
|
||||
private final File projectDir;
|
||||
private final File appDir;
|
||||
private final File srcOutDir;
|
||||
private final File resOutDir;
|
||||
|
||||
private final TaskBarrier barrier;
|
||||
|
||||
public ExportGradleTask(List<ResourceFile> resources, RootNode root, File projectDir, TaskBarrier barrier) {
|
||||
this.resources = resources;
|
||||
this.projectDir = projectDir;
|
||||
this.root = root;
|
||||
this.appDir = new File(projectDir, "app");
|
||||
this.srcOutDir = new File(appDir, "src/main/java");
|
||||
this.resOutDir = new File(appDir, "src/main");
|
||||
this.barrier = barrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
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, projectDir, androidManifest, strings);
|
||||
|
||||
// wait until all sources and resources are exported and all necessary info for gradle export are
|
||||
// collected
|
||||
try {
|
||||
barrier.getTaskCountDown().await();
|
||||
} catch (InterruptedException e) {
|
||||
throw new JadxRuntimeException("Gradle export failed", e);
|
||||
}
|
||||
|
||||
export.generateGradleFiles();
|
||||
}
|
||||
|
||||
public void init() {
|
||||
FileUtils.makeDirs(srcOutDir);
|
||||
FileUtils.makeDirs(resOutDir);
|
||||
}
|
||||
|
||||
public File getSrcOutDir() {
|
||||
return srcOutDir;
|
||||
}
|
||||
|
||||
public File getResOutDir() {
|
||||
return resOutDir;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package jadx.core.export;
|
||||
|
||||
public class GradleInfoStorage {
|
||||
|
||||
private boolean vectorPathData;
|
||||
|
||||
private boolean vectorFillType;
|
||||
|
||||
public boolean isVectorPathData() {
|
||||
return vectorPathData;
|
||||
}
|
||||
|
||||
public void setVectorPathData(boolean vectorPathData) {
|
||||
this.vectorPathData = vectorPathData;
|
||||
}
|
||||
|
||||
public boolean isVectorFillType() {
|
||||
return vectorFillType;
|
||||
}
|
||||
|
||||
public void setVectorFillType(boolean vectorFillType) {
|
||||
this.vectorFillType = vectorFillType;
|
||||
}
|
||||
}
|
||||
@@ -316,6 +316,13 @@ public class BinaryXMLParser extends CommonBinaryParser {
|
||||
decodeAttribute(attributeNS, attrValDataType, attrValData,
|
||||
shortNsName, attrName);
|
||||
}
|
||||
if (shortNsName != null && shortNsName.equals("android")) {
|
||||
if (attrName.equals("pathData")) {
|
||||
rootNode.getGradleInfoStorage().setVectorPathData(true);
|
||||
} else if (attrName.equals("fillType")) {
|
||||
rootNode.getGradleInfoStorage().setVectorFillType(true);
|
||||
}
|
||||
}
|
||||
writer.add('"');
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import org.slf4j.LoggerFactory;
|
||||
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.ResourcesLoader;
|
||||
import jadx.api.TaskBarrier;
|
||||
import jadx.api.plugins.utils.ZipSecurity;
|
||||
import jadx.core.dex.visitors.SaveCode;
|
||||
import jadx.core.utils.exceptions.JadxException;
|
||||
@@ -22,17 +23,29 @@ public class ResourcesSaver implements Runnable {
|
||||
private final ResourceFile resourceFile;
|
||||
private final File outDir;
|
||||
|
||||
private TaskBarrier barrier = null;
|
||||
|
||||
public ResourcesSaver(File outDir, ResourceFile resourceFile) {
|
||||
this.resourceFile = resourceFile;
|
||||
this.outDir = outDir;
|
||||
}
|
||||
|
||||
public ResourcesSaver(File outDir, ResourceFile resourceFile, TaskBarrier barrier) {
|
||||
this.resourceFile = resourceFile;
|
||||
this.outDir = outDir;
|
||||
this.barrier = barrier;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
saveResources(resourceFile.loadContent());
|
||||
} catch (Throwable e) {
|
||||
LOG.warn("Failed to save resource: {}", resourceFile.getOriginalName(), e);
|
||||
} finally {
|
||||
if (barrier != null) {
|
||||
barrier.finishTask();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ android {
|
||||
targetSdkVersion {{targetSdkVersion}}
|
||||
versionCode {{versionCode}}
|
||||
versionName "{{versionName}}"
|
||||
|
||||
{{additionalOptions}}
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
@@ -12,8 +13,10 @@ import jadx.api.ICodeInfo;
|
||||
import jadx.api.JadxDecompiler;
|
||||
import jadx.api.JadxDecompilerTestUtils;
|
||||
import jadx.api.ResourceFile;
|
||||
import jadx.api.TaskBarrier;
|
||||
import jadx.core.dex.nodes.RootNode;
|
||||
import jadx.core.export.ExportGradleProject;
|
||||
import jadx.core.export.ExportGradleTask;
|
||||
import jadx.core.xmlgen.ResContainer;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
@@ -23,6 +26,13 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
public abstract class ExportGradleTest {
|
||||
|
||||
private final RootNode root;
|
||||
|
||||
public ExportGradleTest() {
|
||||
final JadxDecompiler decompiler = JadxDecompilerTestUtils.getMockDecompiler();
|
||||
root = decompiler.getRoot();
|
||||
}
|
||||
|
||||
private static final String MANIFEST_TESTS_DIR = "src/test/manifest";
|
||||
|
||||
@TempDir
|
||||
@@ -47,19 +57,25 @@ public abstract class ExportGradleTest {
|
||||
return contentBuilder.toString();
|
||||
}
|
||||
|
||||
protected RootNode getRootNode() {
|
||||
return root;
|
||||
}
|
||||
|
||||
protected void exportGradle(String manifestFilename, String stringsFileName) {
|
||||
final JadxDecompiler decompiler = JadxDecompilerTestUtils.getMockDecompiler();
|
||||
ResourceFile androidManifest = mock(ResourceFile.class);
|
||||
final ResContainer androidManifestContainer = createResourceContainer(manifestFilename);
|
||||
when(androidManifest.loadContent()).thenReturn(androidManifestContainer);
|
||||
final ResContainer strings = createResourceContainer(stringsFileName);
|
||||
final RootNode root = decompiler.getRoot();
|
||||
TaskBarrier taskBarrier = new TaskBarrier();
|
||||
|
||||
final ExportGradleTask exportGradleTask = new ExportGradleTask(List.of(androidManifest), root, exportDir, taskBarrier);
|
||||
exportGradleTask.init();
|
||||
assertThat(exportGradleTask.getSrcOutDir().exists());
|
||||
assertThat(exportGradleTask.getResOutDir().exists());
|
||||
|
||||
final ExportGradleProject export =
|
||||
new ExportGradleProject(root, exportDir, androidManifest, strings);
|
||||
export.init();
|
||||
assertThat(export.getSrcOutDir().exists());
|
||||
assertThat(export.getResOutDir().exists());
|
||||
export.generateGradleFiles();
|
||||
}
|
||||
|
||||
protected String getAppGradleBuild() {
|
||||
|
||||
@@ -12,7 +12,7 @@ public class OptionalTargetSdkVersion extends ExportGradleTest {
|
||||
void test() {
|
||||
exportGradle("OptionalTargetSdkVersion.xml", "strings.xml");
|
||||
|
||||
assertThat(getAppGradleBuild()).contains("targetSdkVersion 14");
|
||||
assertThat(getAppGradleBuild()).contains("targetSdkVersion 14").doesNotContain(" vectorDrawables.useSupportLibrary = true");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package jadx.tests.export;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import jadx.core.export.GradleInfoStorage;
|
||||
import jadx.tests.api.ExportGradleTest;
|
||||
|
||||
import static jadx.tests.api.utils.assertj.JadxAssertions.assertThat;
|
||||
|
||||
public class VectorDrawablesUseSupportLibrary extends ExportGradleTest {
|
||||
|
||||
@Test
|
||||
void test() {
|
||||
GradleInfoStorage gradleInfo = getRootNode().getGradleInfoStorage();
|
||||
gradleInfo.setVectorFillType(true);
|
||||
exportGradle("OptionalTargetSdkVersion.xml", "strings.xml");
|
||||
assertThat(getAppGradleBuild()).contains(" vectorDrawables.useSupportLibrary = true");
|
||||
|
||||
gradleInfo.setVectorFillType(false);
|
||||
gradleInfo.setVectorPathData(true);
|
||||
exportGradle("OptionalTargetSdkVersion.xml", "strings.xml");
|
||||
assertThat(getAppGradleBuild()).contains(" vectorDrawables.useSupportLibrary = true");
|
||||
|
||||
exportGradle("MinSdkVersion25.xml", "strings.xml");
|
||||
assertThat(getAppGradleBuild()).doesNotContain(" vectorDrawables.useSupportLibrary = true");
|
||||
|
||||
}
|
||||
}
|
||||
@@ -17,6 +17,7 @@ public class TemplateFileTest {
|
||||
tmpl.add("targetSdkVersion", 2);
|
||||
tmpl.add("versionCode", 3);
|
||||
tmpl.add("versionName", "1.2.3");
|
||||
tmpl.add("additionalOptions", "useLibrary 'org.apache.http.legacy'");
|
||||
String res = tmpl.build();
|
||||
System.out.println(res);
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" android:compileSdkVersion="33" android:compileSdkVersionCodename="13" package="jadx.test.app" platformBuildVersionCode="33" platformBuildVersionName="13">
|
||||
<uses-sdk android:minSdkVersion="25" android:targetSdkVersion="32"/>
|
||||
<application android:label="JadxTestApp">
|
||||
</application>
|
||||
</manifest>
|
||||
Reference in New Issue
Block a user