Files
AndroidIDE/app/src/main/java/com/itsaky/androidide/managers/ToolsManager.java
T
2021-12-05 18:12:42 +05:30

344 lines
14 KiB
Java

/************************************************************************************
* This file is part of AndroidIDE.
*
* AndroidIDE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* AndroidIDE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with AndroidIDE. If not, see <https://www.gnu.org/licenses/>.
*
**************************************************************************************/
package com.itsaky.androidide.managers;
import com.blankj.utilcode.util.FileIOUtils;
import com.blankj.utilcode.util.ResourceUtils;
import com.blankj.utilcode.util.ZipUtils;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.Logger;
import java.io.File;
import java.io.IOException;
public class ToolsManager {
private static PreferenceManager prefs;
public static final int JLS_VERSION = 25;
public static final int LOGSENDER_VERSION = 2;
public static final int CLEANER_VERSION = 1;
public static final int GRADLE_API_VERSION = 2;
public static final String KEY_JLS_VERSION = "tools_jlsVersion";
public static final String KEY_LOGSENDER_VERSION = "tools_logsenderVersion";
public static final String KEY_CLEANER_VERSION = "tools_cleanerVersion";
public static final String KEY_GRADLE_API_VERSION = "tools_gradleApiVersion";
public static String ARCH_SPECIFIC_ASSET_DATA_DIR = "data/" + StudioApp.getArch();
public static String COMMON_ASSET_DATA_DIR = "data/common";
public static void init(StudioApp app, Runnable onFinish) {
prefs = app.getPrefManager();
copyBusyboxIfNeeded();
extractJlsIfNeeded();
extractLogsenderIfNeeded();
extractLibHooks();
writeInitScript();
rewriteProjectData();
if(onFinish != null)
onFinish.run();
}
private static void extractLibHooks() {
if(!Environment.LIBHOOK.exists()) {
ResourceUtils.copyFileFromAssets(getArchSpecificAsset("libhook.so"), Environment.LIBHOOK.getAbsolutePath());
}
if(!Environment.LIBHOOK2.exists()) {
ResourceUtils.copyFileFromAssets(getArchSpecificAsset("libhook2.so"), Environment.LIBHOOK2.getAbsolutePath());
}
}
private static void rewriteProjectData() {
FileIOUtils.writeFileFromString(Environment.PROJECT_DATA_FILE, "/**********************/");
}
private static void copyBusyboxIfNeeded() {
File exec = Environment.BUSYBOX;
if(exec.exists()) return;
Environment.mkdirIfNotExits(exec.getParentFile());
ResourceUtils.copyFileFromAssets(getArchSpecificAsset("busybox"), exec.getAbsolutePath());
if(!exec.canExecute()) {
exec.setExecutable(true);
}
}
private static void writeInitScript() {
FileIOUtils.writeFileFromString(Environment.INIT_SCRIPT, INIT_SCRIPT);
}
private static void extractJlsIfNeeded() {
final boolean isOld = JLS_VERSION > prefs.getInt(KEY_JLS_VERSION, 0);
if(!Environment.JLS_JAR.exists() || isOld) {
try {
extractJls();
} catch (Throwable e) {}
}
}
private static void extractJls(){
ResourceUtils.copyFileFromAssets(getCommonAsset("jls.jar"), Environment.JLS_JAR.getAbsolutePath());
}
private static void extractLogsenderIfNeeded() {
try {
final boolean isOld = LOGSENDER_VERSION > prefs.getInt(KEY_LOGSENDER_VERSION, 0);
if(isOld) {
final File logsenderZip = new File(Environment.JLS_HOME, "logsender.zip");
ResourceUtils.copyFileFromAssets(getCommonAsset("logsender.zip"), logsenderZip.getAbsolutePath());
ZipUtils.unzipFile(logsenderZip, Environment.HOME);
prefs.putInt(KEY_LOGSENDER_VERSION, LOGSENDER_VERSION);
}
} catch (IOException e) {}
}
public static String getArchSpecificAsset (String name) {
return ARCH_SPECIFIC_ASSET_DATA_DIR + "/" + name;
}
public static String getCommonAsset (String name) {
return COMMON_ASSET_DATA_DIR + "/" + name;
}
private static final String INIT_SCRIPT =
( "import org.gradle.api.Plugin"
+ "\nimport org.gradle.api.Project"
+ "\nimport groovy.json.*"
+ "\n"
+ "\ngradle.projectsLoaded {"
+ "\n rootProject.subprojects.forEach { sub ->"
+ "\n sub.afterEvaluate {"
+ "\n sub.apply plugin: AndroidIDEPlugin"
+ "\n }"
+ "\n }"
+ "\n}"
+ "\n"
+ "\nclass AndroidIDEPlugin implements Plugin<Project> {"
+ "\n "
+ "\n @Override"
+ "\n void apply(Project project) {"
+ "\n "
+ "\n def isApp = project.getPluginManager().hasPlugin(\"com.android.application\")"
+ "\n def isLibrary = project.getPluginManager().hasPlugin(\"com.android.library\")"
+ "\n "
+ "\n if(!(isApp || isLibrary)) {"
+ "\n println \"Project: \" + project.getName() + \" doesn\'t apply Android\'s application or library plugin\""
+ "\n println \"AndroidIDE\'s plugin will not be applied to this project!\""
+ "\n return"
+ "\n }"
+ "\n "
+ "\n if(isApp) {"
+ "\n project.android {"
+ "\n sourceSets {"
+ "\n main.java.srcDirs += \"${System.getenv(\"HOME\")}/logsender\""
+ "\n }"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n def variants = null"
+ "\n if(isApp) {"
+ "\n variants = project.android.applicationVariants"
+ "\n } else if (isLibrary) {"
+ "\n variants = project.android.libraryVariants"
+ "\n } else {"
+ "\n println \"AndroidIDE: Cannot find applicationVariants or libraryVariants\""
+ "\n return"
+ "\n }"
+ "\n "
+ "\n project.tasks.create("
+ "\n name: \"initializeIDEProject\","
+ "\n group: \"AndroidIDE\","
+ "\n description: \"Initializes the project in AndroidIDE. AndroidIDE manages the proper execution of this task. You\'re not supposed to execute this task manually!\""
+ "\n )"
+ "\n {"
+ "\n outputs.upToDateWhen { false }"
+ "\n "
+ "\n doLast {"
+ "\n def root = project.getRootProject()"
+ "\n "
+ "\n IDEProject ideProject = new IDEProject()"
+ "\n ideProject.name = root.getName()"
+ "\n ideProject.displayName = root.getDisplayName()"
+ "\n ideProject.description = root.getDescription()"
+ "\n ideProject.path = root.getPath()"
+ "\n ideProject.projectDir = root.getProjectDir().getAbsolutePath()"
+ "\n "
+ "\n def tasks = root.tasks"
+ "\n if(tasks != null && tasks.size() > 0) {"
+ "\n addTasks(ideProject, root)"
+ "\n }"
+ "\n "
+ "\n root.subprojects.forEach { sub ->"
+ "\n addIDEProject(ideProject, sub);"
+ "\n }"
+ "\n "
+ "\n File out = new File(\"@@PROJECT_DATA_FILE@@\")"
+ "\n if(out.exists()) {"
+ "\n out.delete()"
+ "\n }"
+ "\n out << new JsonBuilder( ideProject ).toPrettyString()"
+ "\n "
+ "\n println \">>> PROJECT INITIALIZED <<<\""
+ "\n }"
+ "\n }"
+ "\n "
+ "\n project.afterEvaluate {"
+ "\n project.tasks.getByName(\'compileDebugJavaWithJavac\').finalizedBy(\'initializeIDEProject\')"
+ "\n project.tasks.getByName(\'compileReleaseJavaWithJavac\').finalizedBy(\'initializeIDEProject\')"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n def addIDEProject (IDEProject parent, Project sub) {"
+ "\n if(parent == null || sub == null) {"
+ "\n return"
+ "\n }"
+ "\n "
+ "\n def isApp = sub.getPluginManager().hasPlugin(\"com.android.application\")"
+ "\n def isLibrary = sub.getPluginManager().hasPlugin(\"com.android.library\")"
+ "\n "
+ "\n if(!(isApp || isLibrary)) {"
+ "\n return"
+ "\n }"
+ "\n "
+ "\n IDEModule module = new IDEModule()"
+ "\n if(isApp) {"
+ "\n module = new IDEAppModule()"
+ "\n }"
+ "\n "
+ "\n def android = sub.android"
+ "\n def config = android.defaultConfig"
+ "\n "
+ "\n module.name = sub.getName()"
+ "\n module.displayName = sub.getDisplayName()"
+ "\n module.description = sub.getDescription()"
+ "\n module.path = sub.getPath()"
+ "\n module.buildToolsVersion = android.buildToolsVersion"
+ "\n module.compileSdkVersion = android.compileSdkVersion"
+ "\n module.minSdk = config.minSdkVersion"
+ "\n module.targetSdk = config.targetSdkVersion"
+ "\n module.versionCode = config.versionCode"
+ "\n module.versionName = config.versionName"
+ "\n module.projectDir = sub.getProjectDir().getAbsolutePath()"
+ "\n module.viewBindingEnabled = android.buildFeatures.viewBinding"
+ "\n "
+ "\n if(isApp) {"
+ "\n module.applicationId = config.applicationId"
+ "\n }"
+ "\n "
+ "\n def tasks = sub.tasks"
+ "\n if(tasks != null && tasks.size() > 0) {"
+ "\n addTasks(module, sub)"
+ "\n }"
+ "\n "
+ "\n def variants = null"
+ "\n if(isApp) {"
+ "\n variants = sub.android.applicationVariants"
+ "\n } else if (isLibrary) {"
+ "\n variants = sub.android.libraryVariants"
+ "\n } else {"
+ "\n println \"AndroidIDE: Cannot find build variants of project: ${module.name}(${module.path})\""
+ "\n }"
+ "\n "
+ "\n if(variants != null) {"
+ "\n variants.all { variant ->"
+ "\n addDependencies(module, variant);"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n def subs = sub.getSubprojects()"
+ "\n if(subs != null && subs.size() > 0) {"
+ "\n subs.forEach { subOfSub -> "
+ "\n addIDEProject(module, subOfSub)"
+ "\n }"
+ "\n }"
+ "\n parent.modules.add(module)"
+ "\n }"
+ "\n "
+ "\n def addDependencies(def module, def variant) {"
+ "\n variant.getCompileClasspath().each { dependency ->"
+ "\n def path = dependency.absolutePath"
+ "\n if(!module.dependencies.contains(path)) {"
+ "\n module.dependencies.add(dependency.absolutePath);"
+ "\n }"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n def addTasks (IDEProject project, Project gradleProject) {"
+ "\n if(project == null || gradleProject == null) {"
+ "\n return"
+ "\n }"
+ "\n "
+ "\n gradleProject.tasks.forEach { gradleTask -> "
+ "\n IDETask task = new IDETask()"
+ "\n task.name = gradleTask.getName()"
+ "\n task.description = gradleTask.getDescription()"
+ "\n task.group = gradleTask.getGroup()"
+ "\n task.path = gradleTask.getPath()"
+ "\n "
+ "\n project.tasks.add(task)"
+ "\n }"
+ "\n }"
+ "\n}"
+ "\n"
+ "\nclass IDEProject {"
+ "\n def name = \"Not defined\""
+ "\n def displayName = \"Not defined\""
+ "\n def description = \"Not defined\""
+ "\n def path = \"Not defined\""
+ "\n def projectDir = \"Not defined\""
+ "\n List<IDEProject> modules = new ArrayList<IDEProject>()"
+ "\n List<IDETask> tasks = new ArrayList<IDETask>()"
+ "\n List<String> dependencies = new ArrayList<String>()"
+ "\n}"
+ "\n"
+ "\nclass IDEModule extends IDEProject {"
+ "\n def buildToolsVersion = 0"
+ "\n def compileSdkVersion = 0"
+ "\n def minSdk = 0"
+ "\n def targetSdk = 0"
+ "\n def versionCode = 0"
+ "\n def versionName = \"Not defined\""
+ "\n def isLibrary = true"
+ "\n def viewBindingEnabled = false"
+ "\n}"
+ "\n"
+ "\nclass IDEAppModule extends IDEModule {"
+ "\n def applicationId = \"Not defined\""
+ "\n "
+ "\n public IDEAppModule() {"
+ "\n isLibrary = false"
+ "\n }"
+ "\n}"
+ "\n"
+ "\nclass IDETask {"
+ "\n def name = \"Not defined\""
+ "\n def description = \"Not defined\""
+ "\n def group = \"Not defined\""
+ "\n def path = \"Not defined\""
+ "\n}").replace("@@PROJECT_DATA_FILE@@", Environment.PROJECT_DATA_FILE.getAbsolutePath());
private static final Logger LOG = Logger.instance("ToolsManager");
}