Removed usage of Gradle Tooling API

This commit is contained in:
Akash Yadav
2021-09-06 19:14:53 +05:30
parent 994e4e2cec
commit ff70d00db8
14 changed files with 893 additions and 551 deletions
@@ -82,12 +82,6 @@ import com.itsaky.androidide.utils.TransformUtils;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.androidide.views.MaterialBanner;
import com.itsaky.androidide.views.SymbolInputView;
import com.itsaky.gradle.tooling.api.model.IDEProject;
import com.itsaky.gradle.tooling.api.model.Module;
import com.itsaky.gradle.tooling.api.model.build.BuildProgressEvent;
import com.itsaky.gradle.tooling.api.model.build.OperationDescriptor;
import com.itsaky.gradle.tooling.api.model.build.ProgressType;
import com.itsaky.gradle.tooling.api.model.build.descriptors.TaskDescriptor;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.DidChangeTextDocumentParams;
import com.itsaky.lsp.DidChangeWatchedFilesParams;
@@ -233,8 +227,10 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mBinding.fabView.setOnClickListener(v -> showViewOptions());
getApp().checkAndUpdateGradle();
getApp().startBuildService(new File(mProject.getProjectPath()));
getApp().startBuildService(mProject);
getApp().getBuildService().setListener(this);
startServices();
invalidateOptionsMenu();
KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged());
registerLogReceiver();
@@ -295,6 +291,10 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mBinding.viewDaemonStatus.setOnClickListener(v -> showDaemonStatus());
}
private void startServices() {
getBuildService().showDependencies();
}
private void removeFromParent(View v) {
if(v.getParent() != null && v.getParent() instanceof ViewGroup) {
((ViewGroup) v.getParent()).removeView(v);
@@ -320,7 +320,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
private void notifySyncNeeded() {
if(getBuildService() != null && getBuildService().isProjectInitialized() && !getBuildService().isBuilding()) {
if(getBuildService() != null && !getBuildService().isBuilding()) {
getSyncBanner()
.setNegative(android.R.string.cancel, null)
.setPositive(android.R.string.ok, v -> {
@@ -331,7 +331,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
private void closeProject(boolean manualFinish) {
if (getBuildService() != null && getBuildService().isProjectInitialized()) {
if (getBuildService() != null) {
getBuildService().setListener(null);
getBuildService().exit();
}
@@ -409,7 +409,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
for (int id : BUILD_IDS) {
MenuItem item = menu.findItem(id);
if (item != null) {
boolean enabled = getBuildService() != null && getBuildService().isProjectInitialized() && !getBuildService().isBuilding();
boolean enabled = getBuildService() != null && !getBuildService().isBuilding();
item.setEnabled(enabled);
item.getIcon().setAlpha(enabled ? 255 : 76);
}
@@ -420,8 +420,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
MenuItem undo = menu.findItem(R.id.menuEditor_undo);
MenuItem redo = menu.findItem(R.id.menuEditor_redo);
MenuItem save = menu.findItem(R.id.menuEditor_save);
MenuItem def = menu.findItem(R.id.menuEditor_gotoDefinition);
MenuItem ref = menu.findItem(R.id.menuEditor_findReferences);
MenuItem def = menu.findItem(R.id.menuEditor_gotoDefinition);
MenuItem ref = menu.findItem(R.id.menuEditor_findReferences);
MenuItem comment = menu.findItem(R.id.menuEditor_commentLine);
MenuItem uncomment = menu.findItem(R.id.menuEditor_uncommentLine);
MenuItem findFile = menu.findItem(R.id.menuEditor_findFile);
@@ -624,114 +624,107 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
@Override
public void stdOut(GradleTask task, CharSequence text) {
if(text == null) text = "";
appendBuildOut(text.toString(), STD_TYPE_NORMAL);
}
@Override
public void stdErr(GradleTask task, CharSequence text) {
if(text == null) text = "";
appendBuildOut(text.toString(), STD_TYPE_ERROR);
}
@Override
public void stdSuccess(GradleTask task, CharSequence text) {
if(text == null) text = "";
appendBuildOut(text.toString(), STD_TYPE_SUCCESS);
public void appendOutput(GradleTask task, CharSequence text) {
if(text == null)
return;
if(task != null && !task.canOutput()) {
return;
}
appendBuildOut(text.toString());
}
@Override
public void prepare() {
boolean isFirstBuild = getApp().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true);
setStatus(getString(isFirstBuild ? R.string.preparing_first : R.string.preparing));
if(isFirstBuild) {
showFirstBuildNotice();
}
}
@Override
public void onConnectionProgressChanged(String status) {
setStatus(status);
}
@Override
public void onBuildProgress(GradleTask gradleTask, BuildProgressEvent event) {
OperationDescriptor descriptor = event.descriptor;
if(event.descriptor != null) {
/**
* A descriptor describes a progress event
* Different descriptors are available for different events in package {@code com.itsaky.gradle.tooling.api.model.build.descriptors }
*/
if(event.descriptor instanceof TaskDescriptor) {
TaskDescriptor task = (TaskDescriptor) descriptor;
setStatus(String.format("> Task %s", task.taskPath));
}
public void prepare() {
boolean isFirstBuild = getApp().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true);
setStatus(getString(isFirstBuild ? R.string.preparing_first : R.string.preparing));
if(isFirstBuild) {
showFirstBuildNotice();
}
}
@Override
public void onBuildSuccessful(GradleTask task, String msg) {
invalidateOptionsMenu();
if(task == null) return;
if(task.canOutput())
setStatus(msg);
if(task.getType() == GradleTask.Type.BUILD) {
if(task.getTaskID() == IDEService.TASK_ASSEMBLE_DEBUG) {
if(task.buildsApk()) {
install(task.getApk(new File(mProject.getMainModulePath(), "build").getAbsolutePath(), mProject.getMainModule()));
}
}
}
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
}
@Override
public void onBuildFailed(GradleTask task, String msg) {
invalidateOptionsMenu();
if(task == null) return;
if(task.canOutput()) {
setStatus(msg);
}
showBuildResult();
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
}
@Override
public void onProjectInitialized(IDEProject project, Module main) {
public void onStartingGradleDaemon(GradleTask task) {
setStatus(getString(R.string.msg_starting_daemon));
getApp().setStopGradleDaemon(false);
}
@Override
public void onRunTask(GradleTask task, String taskName) {
if(task != null && task.canOutput()) {
setStatus(getBuildService().typeString(task.getTaskID()) + " " + taskName);
}
}
@Override
public void onBuildSuccessful(GradleTask task, String msg) {
if(task == null) return;
if(task.canOutput())
setStatus(msg);
if(task.getType() == GradleTask.Type.BUILD) {
if(task.getTaskID() == IDEService.TASK_ASSEMBLE_DEBUG) {
if(task.buildsApk()) {
install(task.getApk(new File(mProject.getMainModulePath(), "build").getAbsolutePath(), mProject.getMainModule()));
}
}
}
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
invalidateOptionsMenu();
}
@Override
public void onBuildFailed(GradleTask task, String msg) {
if(task == null) return;
if(task.canOutput()) {
setStatus(msg);
}
showBuildResult();
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
invalidateOptionsMenu();
}
@Override
public void onGetDependencies(List<String> dependencies) {
setStatus(getString(R.string.msg_starting_completion));
mProject.setClassPaths(new ArrayList<String>(main.dependencyJars));
mProject.setClassPaths(dependencies);
createServices();
}
@Override
public void saveFiles() {
saveAll();
}
@Override
public void onGetDependenciesFailed() {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog);
builder.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, (p1, p2) -> {
p1.dismiss();
if(getBuildService() != null)
getBuildService().showDependencies();
})
.setTitle(R.string.failed)
.setMessage(R.string.msg_first_prepare_failed)
.create().show();
}
@Override
public void saveFiles() {
saveAll();
}
// Can be called from a different different, better to run on UI thread
private void appendBuildOut(final String str, int type) {
runOnUiThread(() -> {
private void appendBuildOut(final String str) {
runOnUiThread(() -> {
String strFinal = str.endsWith("\n") ? str : str.concat("\n");
int line = getBuildView(true).getText().append(strFinal);
if(type == STD_TYPE_SUCCESS) {
((BuildOutputLanguage) getBuildView().getEditorLanguage()).addSuccess(line);
} else if(type == STD_TYPE_ERROR) {
((BuildOutputLanguage) getBuildView().getEditorLanguage()).addError(line);
}
getBuildView(true).getText().append(strFinal);
});
}
}
// Can be called from a different different, better to run on UI thread
private void appendLogOut(final String str) {
runOnUiThread(() -> {
private void appendLogOut(final String str) {
runOnUiThread(() -> {
String strFinal = str.endsWith("\n") ? str : str.concat("\n");
getLogView(true).getText().append(strFinal);
});
@@ -153,13 +153,12 @@ public class StudioApp extends MultiDexApplication
newShell(null).bgAppend("gradle --stop");
}
public void startBuildService(File rootProject) {
public void startBuildService(AndroidProject project) {
if(getBuildService() != null) {
getBuildService().exit();
}
buildService = new IDEService(rootProject);
buildService.start();
buildService = new IDEService(project);
}
public IDEService getBuildService() {
@@ -33,8 +33,6 @@ public class ToolsManager {
copyBusyboxIfNeeded();
extractJlsIfNeeded();
extractLogsenderIfNeeded();
extractCleanerIfNeeded();
extractGradleApiIfNeeded();
writeInitScript();
writeBashrc();
@@ -108,124 +106,224 @@ public class ToolsManager {
} catch (IOException e) {}
}
private static void extractCleanerIfNeeded() {
File cleanerZip = new File(Environment.TMP_DIR, "bin.zip");
File cleaner = new File(Environment.BINDIR, "androidide-cleaner");
final boolean isOld = CLEANER_VERSION > prefs.getInt(KEY_CLEANER_VERSION, 0);
if(!cleaner.exists() || isOld) {
ResourceUtils.copyFileFromAssets("data/bin.zip", cleanerZip.getAbsolutePath());
try {
ZipUtils.unzipFile(cleanerZip, Environment.BINDIR);
prefs.putInt(KEY_CLEANER_VERSION, CLEANER_VERSION);
} catch (Throwable e) {}
}
if(cleaner.exists() && !cleaner.canExecute())
cleaner.setExecutable(true);
}
private static void extractGradleApiIfNeeded() {
final File libs = Environment.GRADLE_API_LIBS_DIR;
final File gradleZip = new File(Environment.TMP_DIR, "gradle-api.zip");
final boolean isOld = GRADLE_API_VERSION > prefs.getInt(KEY_GRADLE_API_VERSION, 0);
if(!libs.exists() || isOld) {
try {
ResourceUtils.copyFileFromAssets("data/gradle-api.zip", gradleZip.getAbsolutePath());
ZipFile file = new ZipFile(gradleZip, ConstantsBridge.GRADLE_API_ZIP_PASSWORD_HASH.toCharArray());
file.extractAll(Environment.GRADLE_API_DIR.getAbsolutePath());
FileUtils.delete(gradleZip);
prefs.putInt(KEY_GRADLE_API_VERSION, GRADLE_API_VERSION);
} catch (Throwable th) { }
}
}
private static final String BASHRC =
"PROMPT_DIRTRIM=2" +
"PS1=\'\\[\\e[0;34m\\]\\w\\[\\e[0m\\] \\[\\e[0;97m\\]$\\[\\e[0m\\] \'";
private static final String INIT_SCRIPT =
"/**"
+ "\n * This is the init script that AndroidIDE uses to configure builds."
+ "\n * "
+ "\n * DO NOT MODIFY"
+ "\n * Any modification will be overwritten!"
+ "\n *"
+ "\n * If you\'re here, most probably you\'re trying to look into how AndroidIDE works"
+ "\n * or maybe you\'re trying to modify AndroidIDE"
+ "\n * Only thing I can say, is, FUCK YOU!"
+ "\n */"
+ "\n"
+ "\nimport org.gradle.api.Plugin"
"import org.gradle.api.Plugin"
+ "\nimport org.gradle.api.Project"
+ "\n"
+ "\ninitscript {"
+ "\n repositories {"
+ "\n maven { url \'https://maven.aliyun.com/repository/public/\' } "
+ "\n maven { url \'https://maven.aliyun.com/repository/google/\' }"
+ "\n maven { url \'https://maven.aliyun.com/repository/gradle-plugin/\' }"
+ "\n maven { url \'https://clojars.org/repo/\'}"
+ "\n maven { url \'https://jitpack.io\'}"
+ "\n maven { url \'https://maven.google.com\' }"
+ "\n google()"
+ "\n mavenCentral()"
+ "\n mavenLocal()"
+ "\n jcenter()"
+ "\n }"
+ "\n "
+ "\n dependencies {"
+ "\n classpath \"com.android.tools.build:gradle:7.0.0\""
+ "\n }"
+ "\n}"
+ "\nimport groovy.json.*"
+ "\n"
+ "\ngradle.projectsLoaded {"
+ "\n rootProject.subprojects.forEach { sub ->"
+ "\n sub.afterEvaluate {"
+ "\n if(sub.getPluginManager().hasPlugin(\"com.android.application\")) {"
+ "\n sub.getPluginManager().apply(AndroidIDEPlugin.class)"
+ "\n }"
+ "\n sub.apply plugin: AndroidIDEPlugin"
+ "\n }"
+ "\n }"
+ "\n}"
+ "\n"
+ "\nclass AndroidIDEPlugin implements Plugin<Project> {"
+ "\n "
+ "\n def AAPT2_VERSION = \"4.0.1-6197926\""
+ "\n "
+ "\n @Override"
+ "\n void apply(Project project) {"
+ "\n "
+ "\n project.android {"
+ "\n sourceSets {"
+ "\n main.java.srcDirs += \"${System.getenv(\"HOME\")}/logsender\""
+ "\n }"
+ "\n }"
+ "\n "
+ "\n project.configurations.matching { it.name == \'_internal_aapt2_binary\' }.all { config ->"
+ "\n config.resolutionStrategy.eachDependency { details ->"
+ "\n details.useVersion(\"$AAPT2_VERSION\")"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n project.android.applicationVariants.all { variant ->"
+ "\n project.tasks.create(name: \"printDependencies${variant.name.capitalize()}\", group: \"Android IDE\", description: \"[AndroidIDE] List all dependencies in project including those in subprojects for ${variant.name.capitalize()} build.\") {"
+ "\n doLast {"
+ "\n showDependencies(project, variant)"
+ "\n }"
+ "\n }"
+ "\n }"
+ "\n }"
+ "\ngradle.projectsEvaluated {"
+ "\n IDEProject project = new IDEProject()"
+ "\n project.name = rootProject.getName()"
+ "\n project.displayName = rootProject.getDisplayName()"
+ "\n project.description = rootProject.getDescription()"
+ "\n project.path = rootProject.getPath()"
+ "\n "
+ "\n def tasks = rootProject.tasks"
+ "\n if(tasks != null && tasks.size() > 0) {"
+ "\n addTasks(project, rootProject)"
+ "\n }"
+ "\n "
+ "\n rootProject.subprojects.forEach { sub ->"
+ "\n addIDEProject(project, sub);"
+ "\n }"
+ "\n "
+ "\n File out = new File(\"" + Environment.PROJECT_DATA_FILE.getAbsolutePath() + "\")"
+ "\n out << new JsonBuilder( project ).toPrettyString()"
+ "\n "
+ "\n println \">>> PROJECT INITIALIZED <<<\""
+ "\n}"
+ "\n"
+ "\ndef showDependencies(def project, def variant) {"
+ "\n println \">> ${variant.name.capitalize()} dependencies <<\""
+ "\n variant.getCompileClasspath().each { dep ->"
+ "\n println dep.absolutePath"
+ "\n }"
+ "\n println \"${System.getenv(\'ANDROID_HOME\')}/platforms/${project.android.compileSdkVersion}/android.jar\""
+ "\ndef 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 "
+ "\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 = project.android.applicationVariants"
+ "\n } else if (isLibrary) {"
+ "\n variants = project.android.libraryVariants"
+ "\n } else {"
+ "\n println \"AndroidIDE: Cannot build variants of project: ${module.name}\""
+ "\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"
+ "\ndef 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"
+ "\ndef 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"
+ "\nclass AndroidIDEPlugin implements Plugin<Project> {"
+ "\n "
+ "\n def AAPT2_VERSION = \"4.0.1-6197926\""
+ "\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 project.android {"
+ "\n sourceSets {"
+ "\n main.java.srcDirs += \"${System.getenv(\"HOME\")}/logsender\""
+ "\n }"
+ "\n }"
+ "\n "
+ "\n project.configurations.matching { it.name == \'_internal_aapt2_binary\' }.all { config ->"
+ "\n config.resolutionStrategy.eachDependency { details ->"
+ "\n details.useVersion(\"$AAPT2_VERSION\")"
+ "\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 variants.all { variant ->"
+ "\n project.tasks.create(name: \"printDependencies${variant.name.capitalize()}\", group: \"Android IDE\", description: \"[AndroidIDE] List all dependencies in project including those in subprojects for ${variant.name.capitalize()} build.\") {"
+ "\n doLast {"
+ "\n printDependencies(project, variant)"
+ "\n }"
+ "\n }"
+ "\n }"
+ "\n }"
+ "\n "
+ "\n def printDependencies(def project, def variant) {"
+ "\n println \">> DEPENDENCIES START <<\""
+ "\n variant.getCompileClasspath().each { dep ->"
+ "\n println \"{ \\\"file\\\": \\\"${dep.absolutePath}\\\"}\""
+ "\n }"
+ "\n println \"{ \\\"type\\\":\\\"ANDROID_JAR\\\", \\\"file\\\": \\\"${System.getenv(\'ANDROID_HOME\')}/platforms/${project.android.compileSdkVersion}/android.jar\\\"}\""
+ "\n println \">> DEPENDENCIES END <<\""
+ "\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 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}"
+ "\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}";
private static final Logger LOG = Logger.instance("ToolsManager");
@@ -0,0 +1,19 @@
package com.itsaky.androidide.models.project;
import com.google.gson.annotations.SerializedName;
/**
* Represents an Android application project
*/
public class IDEAppModule extends IDEModule {
/**
* Library projects don't have applicationId, application projects do.
*/
@SerializedName("applicationId")
public String applicationId;
public IDEAppModule () {
isLibrary = false;
}
}
@@ -0,0 +1,53 @@
package com.itsaky.androidide.models.project;
import com.google.gson.annotations.SerializedName;
/**
* Represents a module (subproject) of a root project.
*
* In Android projects, a module can either be an application project,
* or a library project. Application projects are represented by IDEAppModule
*/
public class IDEModule extends IDEProject {
/**
* Build tools version of this project
*/
@SerializedName("buildToolsVersion")
int buildToolsVersion = 0;
/**
* Compile SDK version of this project
*/
@SerializedName("compileSdkVersion")
int compileSdkVersion = 0;
/**
* Minimum SDK version of this project
*/
@SerializedName("minSdk")
int minSdk = 0;
/**
* Target SDK version of this project
*/
@SerializedName("targetSdk")
int targetSdk = 0;
/**
* Version code of this project
*/
@SerializedName("versionCode")
int versionCode = 0;
/**
* Version name of this project
*/
@SerializedName("versionName")
String versionName = "Not defined";
/**
* Is this project an Android library project?
*/
@SerializedName("isLibrary")
boolean isLibrary = true;
}
@@ -0,0 +1,54 @@
package com.itsaky.androidide.models.project;
import com.google.gson.annotations.SerializedName;
import java.util.List;
import java.util.ArrayList;
/**
* A model that contains data of a project currently opened in AndroidIDE
*/
public class IDEProject {
/**
* Name of this project
*/
@SerializedName("name")
public String name;
/**
* Name of this project which should be displayed to user
*/
@SerializedName("displayName")
public String displayName;
/**
* Description of this project. Maybe null.
*/
@SerializedName("description")
public String description;
/**
* Path of this project. This is NOT the directory path of this project.
*
* For example, ':app: or ':module:subModule'
*/
@SerializedName("path")
public String path;
/**
* Subprojects of this project
*/
@SerializedName("modules")
public List<IDEProject> modules = new ArrayList<>();
/**
* Tasks included in this project
*/
@SerializedName("tasks")
public List<IDETask> tasks = new ArrayList<>();
/**
* Path of jar file of dependencies of this project
*/
@SerializedName("dependencies")
public List<String> dependencies = new ArrayList<>();
}
@@ -0,0 +1,37 @@
package com.itsaky.androidide.models.project;
import com.google.gson.annotations.SerializedName;
/**
* Represents a Gradle task
*/
public class IDETask {
/**
* Name of this task
*/
@SerializedName("name")
public String name;
/**
* Description of this task
*/
@SerializedName("description")
public String description;
/**
* Name of the group that this task belongs to
*/
@SerializedName("group")
public String group;
/**
* Path of this task
*
* For example, ':app:assembleDebug', ':app:build'
*/
@SerializedName("path")
public String path;
}
@@ -1,45 +1,18 @@
package com.itsaky.androidide.services;
import android.text.TextUtils;
import androidx.annotation.StringRes;
import com.blankj.utilcode.util.CloseUtils;
import com.blankj.utilcode.util.FileUtils;
import com.google.gson.GsonBuilder;
import com.itsaky.androidide.R;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.sockets.SocketConnection;
import com.itsaky.androidide.tasks.GradleTask;
import com.itsaky.androidide.tasks.gradle.BaseGradleTasks;
import com.itsaky.androidide.tasks.gradle.build.AssembleDebug;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.FileUtil;
import com.itsaky.gradle.tooling.api.GradleConnection;
import com.itsaky.gradle.tooling.api.messages.ErrorMessage;
import com.itsaky.gradle.tooling.api.messages.Message;
import com.itsaky.gradle.tooling.api.messages.Method;
import com.itsaky.gradle.tooling.api.messages.ResponseMessage;
import com.itsaky.gradle.tooling.api.messages.params.BuildParams;
import com.itsaky.gradle.tooling.api.messages.params.InitializeParams;
import com.itsaky.gradle.tooling.api.model.IDEProject;
import com.itsaky.gradle.tooling.api.model.Module;
import com.itsaky.gradle.tooling.api.model.build.BuildProgressEvent;
import com.itsaky.gradle.tooling.api.model.build.BuildResult;
import com.itsaky.gradle.tooling.api.model.build.OperationDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.FinishEventDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.ProjectConfigurationDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.StartEventDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.StatusEventDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.TaskDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.TestOutDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.TransformDescriptor;
import com.itsaky.gradle.tooling.api.model.build.descriptors.WorkItemDescriptor;
import com.itsaky.gradle.tooling.api.model.build.identifiers.BinaryPluginIdentifierDescription;
import com.itsaky.gradle.tooling.api.model.build.identifiers.PluginIdentifierDescription;
import com.itsaky.gradle.tooling.api.model.build.identifiers.ScriptPluginIdentifierDescription;
import com.itsaky.gradle.tooling.api.tools.RuntimeTypeAdapterFactory;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
@@ -47,349 +20,339 @@ import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.stream.Collectors;
import org.json.JSONObject;
public class IDEService extends SocketConnection implements ShellServer.Callback {
public class IDEService implements ShellServer.Callback {
private ShellServer shell;
private BuildListener listener;
private File rootProject;
private GradleTask currentTask;
private ShellServer shell;
private BuildListener listener;
private GradleTask currentTask;
private StudioApp app;
private IDEProject project;
private Module main;
private boolean isConnected;
private boolean projectInitialized = false;
private boolean isBuilding = false;
private AndroidProject project;
private List<String> dependencies;
private boolean isBuilding = false;
private boolean isRunning = false;
private long buildStart = 0;
private int UNIVERSAL_ID = 0;
private boolean outputtingJars = false;
private boolean recreateShellOnDone = false;
public static final String SUCCESSFUL = "SUCCESSFUL";
public static final String FAILED = "FAILED";
private final String RUN_TASK = "> Task";
private final String STARTING_DAEMON = "Starting a ";
private final String BUILD_SUCCESS = "BUILD SUCCESSFUL";
private final String BUILD_FAILED = "BUILD FAILED";
private final String SHOW_DEPENDENCIES_START = ">> DEPENDENCIES START <<";
private final String SHOW_DEPENDENCIES_END = ">> DEPENDENCIES END <<";
private final String PROJECT_INITIALIZED = ">>> PROJECT INITIALIZED <<<";
public static final int TASK_SHOW_DEPENDENCIES = 0;
public static final int TASK_ASSEMBLE_DEBUG = 1;
public static final int TASK_ASSEMBLE_RELEASE = 2;
public static final int TASK_BUILD = 3;
public static final int TASK_BUNDLE = 4;
public static final int TASK_CLEAN = 5;
public static final int TASK_CLEAN_BUILD = 6;
public static final int TASK_COMPILE_JAVA = 7;
public static final int TASK_DEX = 8;
public static final int TASK_LINT = 9;
public static final int TASK_LINT_DEBUG = 10;
public static final int TASK_LINT_RELEASE = 11;
private static final String START_COMMAND = "java " +
"-cp %s" +
" com.itsaky.gradle.tooling.api.GradleApi";
static {
RuntimeTypeAdapterFactory<OperationDescriptor> descriptorFactory = RuntimeTypeAdapterFactory.of(OperationDescriptor.class)
.registerSubtype(OperationDescriptor.class, "OperationDescriptor")
.registerSubtype(TaskDescriptor.class, "TaskDescriptor")
.registerSubtype(FinishEventDescriptor.class, "FinishEventDescriptor")
.registerSubtype(ProjectConfigurationDescriptor.class, "ProjectConfigurationDescriptor")
.registerSubtype(StartEventDescriptor.class, "StartEventDescriptor")
.registerSubtype(StatusEventDescriptor.class, "StatusEventDescriptor")
.registerSubtype(TestOutDescriptor.class, "TestOutDescriptor")
.registerSubtype(TransformDescriptor.class, "TransformDescriptor")
.registerSubtype(WorkItemDescriptor.class, "WorkItemDescriptor");
RuntimeTypeAdapterFactory<PluginIdentifierDescription> pluginFactory = RuntimeTypeAdapterFactory.of(PluginIdentifierDescription.class)
.registerSubtype(PluginIdentifierDescription.class, "PluginIdentifierDescription")
.registerSubtype(BinaryPluginIdentifierDescription.class, "BinaryPluginIdentifierDescription")
.registerSubtype(ScriptPluginIdentifierDescription.class, "ScriptPluginIdentifierDescription");
GSON = new GsonBuilder()
.registerTypeAdapterFactory(descriptorFactory)
.registerTypeAdapterFactory(pluginFactory)
.create();
}
public IDEService(File rootProject) {
super(GradleConnection.SERVER_HOST, GradleConnection.SERVER_PORT);
public static final int TASK_SHOW_DEPENDENCIES = GradleTask.TASK_SHOW_DEPENDENCIES;
public static final int TASK_ASSEMBLE_DEBUG = GradleTask.TASK_ASSEMBLE_DEBUG;
public static final int TASK_ASSEMBLE_RELEASE = GradleTask.TASK_ASSEMBLE_RELEASE;
public static final int TASK_BUILD = GradleTask.TASK_BUILD;
public static final int TASK_BUNDLE = GradleTask.TASK_BUNDLE;
public static final int TASK_CLEAN = GradleTask.TASK_CLEAN;
public static final int TASK_CLEAN_BUILD = GradleTask.TASK_CLEAN_BUILD;
public static final int TASK_COMPILE_JAVA = GradleTask.TASK_COMPILE_JAVA;
public static final int TASK_DEX = GradleTask.TASK_DEX;
public static final int TASK_LINT = GradleTask.TASK_LINT;
public static final int TASK_LINT_DEBUG = GradleTask.TASK_LINT_DEBUG;
public static final int TASK_LINT_RELEASE = GradleTask.TASK_LINT_RELEASE;
public IDEService(AndroidProject project) {
this.app = StudioApp.getInstance();
this.rootProject = rootProject;
this.project = project;
this.shell = app.newShell(this);
this.shell.bgAppend(String.format(START_COMMAND, getClasspaths()));
isRunning = true;
}
}
public IDEService setListener(BuildListener listener) {
this.listener = listener;
return this;
}
private String getClasspaths() {
final List<File> libs = FileUtils.listFilesInDir(Environment.GRADLE_API_LIBS_DIR, true);
StringBuilder sb = new StringBuilder();
for (int i=0;i < libs.size();i++) {
if (i == libs.size() - 1) {
sb.append(libs.get(i));
} else {
sb.append(libs.get(i) + File.pathSeparator);
@Override
public void output(CharSequence charSequence) {
if(listener == null || charSequence == null) return;
final String line = charSequence
.toString()
.replace(":showDependenciesDebug", ":startCompletionServices")
.trim();
if(line.contains(PROJECT_INITIALIZED)) {
readIdeProject();
return;
}
boolean shouldOutput = true;
if(line.contains(SHOW_DEPENDENCIES_START)) {
shouldOutput = false;
dependencies = new ArrayList<>();
outputtingJars = true;
if(listener != null)
listener.onRunTask(currentTask, "> Task :app:startCompletionServices");
return;
} else if(line.contains(SHOW_DEPENDENCIES_END)) {
shouldOutput = false;
outputtingJars = false;
if(listener != null
&& dependencies != null) {
listener.onGetDependencies(dependencies);
}
StudioApp.getInstance().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
return;
}
return sb.toString();
}
if(outputtingJars) {
JSONObject o = asObjectOrNull(line);
try {
if(o.has("type")) {
String type = o.getString("type");
if(type.equals("ANDROID_JAR")) {
shouldOutput = false;
Environment.setBootClasspath(new File(o.getString("requested")));
}
} else if(o.has("file")) {
shouldOutput = false;
String jar = o.getString("file");
@Override
protected void onConnected() {
isConnected = true;
writeMessage(Method.PROJECT_INIT, new InitializeParams(Environment.GRADLE_HOME.getAbsolutePath(), rootProject.getAbsolutePath()));
}
@Override
protected void onDisconnected() {
LOG.i("Disconnected from server...");
}
@Override
protected void onFailedToConnect(Throwable exception) {
LOG.e(ex(exception));
}
protected void onResponse(ResponseMessage message) {
if (message.result == null) return;
switch (message.method) {
case Method.API_CONNECTION_PROGRESS :
listener.onConnectionProgressChanged(message.result.getAsString());
break;
case Method.PROJECT_INITIALIZED :
project = GSON.fromJson(message.result, IDEProject.class);
List<Module> applicationModules = project.modules.stream().filter(m -> !m.isLibrary).collect(Collectors.toList());
if (applicationModules != null && applicationModules.size() > 0) {
// TODO: Handle multiple application modules
main = applicationModules.get(0);
projectInitialized = true;
listener.onProjectInitialized(project, main);
} else {
// TODO: Notify users about no application modules
dependencies.add(jar);
}
break;
case Method.BUILD_STATUS_CHANGED :
BuildProgressEvent progress = GSON.fromJson(message.result, BuildProgressEvent.class);
listener.onBuildProgress(currentTask, progress);
break;
case Method.BUILD_STD_OUT :
listener.stdOut(currentTask, message.result.getAsString());
break;
case Method.BUILD_STD_ERROR :
listener.stdErr(currentTask, message.result.getAsString());
break;
case Method.BUILD_RESULT :
isBuilding = false;
BuildResult result = GSON.fromJson(message.result, BuildResult.class);
int seconds = (int) (System.currentTimeMillis() - buildStart) / 1000;
String msg = getString(com.itsaky.androidide.R.string.msg_final_build_result, result.isSuccess ? SUCCESSFUL : FAILED, currentTask.getName(), seconds);
if(result.isSuccess) {
listener.stdSuccess(currentTask, getString(com.itsaky.androidide.R.string.msg_final_build_result_prefix));
listener.stdSuccess(currentTask, msg);
listener.onBuildSuccessful(currentTask, msg);
} else {
listener.stdErr(currentTask, getString(com.itsaky.androidide.R.string.msg_final_build_result_prefix));
listener.stdErr(currentTask, msg);
listener.onBuildFailed(currentTask, msg);
}
currentTask = null;
break;
case Method.API_ERROR :
ErrorMessage error = GSON.fromJson(message.result, ErrorMessage.class);
listener.stdErr(currentTask, String.format("========== AndroidIDE Error ==========\nError code: %d\nError message: %s\n======================================", error.code, error.message));
break;
} catch (Throwable th) {}
}
if(shouldOutput) {
String text = line.replace(StudioApp.getInstance().getRootDir().getAbsolutePath(), "ANDROIDIDE_HOME");
listener.appendOutput(currentTask, text);
}
if(charSequence.toString().contains(RUN_TASK)) {
listener.onRunTask(currentTask, charSequence.toString().trim());
} else
if(charSequence.toString().startsWith(STARTING_DAEMON)) {
listener.onStartingGradleDaemon(currentTask);
} else
if(charSequence.toString().contains(BUILD_SUCCESS)) {
isBuilding = false;
outputtingJars = false;
if(recreateShellOnDone)
createShell();
if(currentTask != null && currentTask.getTaskID() != TASK_SHOW_DEPENDENCIES)
listener.onBuildSuccessful(currentTask, charSequence.toString().trim());
appendOutputSeparator();
} else
if(charSequence.toString().contains(BUILD_FAILED)) {
isBuilding = false;
outputtingJars = false;
if(recreateShellOnDone)
createShell();
if(currentTask != null) {
if(currentTask.getTaskID() == TASK_SHOW_DEPENDENCIES)
listener.onGetDependenciesFailed();
else listener.onBuildFailed(currentTask, line.trim());
}
appendOutputSeparator();
execTask(BaseGradleTasks.TASKS);
}
}
@Override
protected void onResponse(String line) {
// writeOut(line.trim() + "\n");
onResponse(GSON.fromJson(line, ResponseMessage.class));
}
private BufferedOutputStream fos;
private void writeOut(String line) {
try {
if (fos == null)
fos = new BufferedOutputStream(new FileOutputStream(new File(FileUtil.getExternalStorageDir(), "ide_xlog/gradle-api-out.txt"), true));
fos.write(line.getBytes());
fos.flush();
} catch (Throwable th) {}
}
public IDEService setListener(BuildListener listener) {
this.listener = listener;
return this;
}
@Override
public void output(CharSequence charSequence) {
// writeOut(charSequence.toString().trim() + "\n");
private void readIdeProject() {
}
private void appendOutputSeparator() {
listener.appendOutput(currentTask, "\n\n");
}
private void createShell() {
if(shell != null)
shell.exit();
shell = app.newShell(this);
recreateShellOnDone = false;
}
private JSONObject asObjectOrNull(String line) {
try {
if(line == null) return null;
line = line.trim();
if(line.length() <= 0) return null;
return new JSONObject(line);
} catch (Throwable th) {
return null;
}
}
public void exit() {
if (mSocket != null) {
CloseUtils.closeIOQuietly(mSocket);
}
// Delete the tmp directory. It is not needed anymore...
FileUtils.delete(Environment.TMP_DIR);
isRunning = false;
isRunning = false;
}
private String currentTime() {
String pattern = "HH:mm:ss";
DateFormat df = new SimpleDateFormat(pattern, Locale.US);
Date today = Calendar.getInstance().getTime();
return df.format(today);
}
private String currentTime() {
String pattern = "HH:mm:ss";
DateFormat df = new SimpleDateFormat(pattern, Locale.US);
Date today = Calendar.getInstance().getTime();
return df.format(today);
}
private String getString(@StringRes int id) {
return app.getString(id);
}
private String getString(@StringRes int id, Object... format) {
return app.getString(id, format);
}
private List<String> getArguments() {
final PreferenceManager prefs = app.getPrefManager();
final List<String> args = new ArrayList<>();
private String getArguments(List<String> tasks) {
final PreferenceManager prefs = app.getPrefManager();
final List<String> args = new ArrayList<>();
args.add("gradle");
args.addAll(tasks);
args.add("--init-script");
args.add(Environment.INIT_SCRIPT.getAbsolutePath());
if (prefs.isStracktraceEnabled()) {
args.add("--stacktrace");
}
if (prefs.isGradleInfoEnabled()) {
args.add("--info");
}
if (prefs.isGradleDebugEnabled()) {
args.add("--debug");
}
if (prefs.isGradleScanEnabled()) {
args.add("--scan");
}
if (prefs.isGradleWarningEnabled()) {
args.add("--warning-mode");
if (prefs.isStracktraceEnabled()) {
args.add("--stacktrace");
}
if (prefs.isGradleInfoEnabled()) {
args.add("--info");
}
if (prefs.isGradleDebugEnabled()) {
args.add("--debug");
}
if (prefs.isGradleScanEnabled()) {
args.add("--scan");
}
if (prefs.isGradleWarningEnabled()) {
args.add("--warning-mode");
args.add("all");
}
}
return TextUtils.join(" ", args);
}
public void execTask(GradleTask task) {
execTask(task, false);
}
public void execTask(GradleTask task, boolean stopIfRunning) {
if (stopIfRunning && isBuilding()) {
stopAllDaemons();
} else if (isBuilding()) {
return;
}
return args;
}
public void execTask(GradleTask task) {
execTask(task, false);
}
public void execTask(GradleTask task, boolean stopIfRunning) {
if (stopIfRunning && isBuilding()) {
stopAllDaemons();
} else if (isBuilding()) {
return;
}
buildStart = System.currentTimeMillis();
if (listener != null) {
listener.saveFiles();
currentTask = task;
BuildParams params = new BuildParams(":" + main.name, task.getTasks(), getArguments());
writeMessage(Method.PROJECT_BUILD, params);
if (listener != null) {
listener.saveFiles();
Environment.mkdirIfNotExits(Environment.TMP_DIR);
currentTask = task;
listener.appendOutput(task, getString(R.string.msg_task_begin, currentTime(), task.getName()));
shell.bgAppend(String.format("cd \"%s\"", new File(project.getProjectPath(), "app").getAbsolutePath()));
shell.bgAppend(getArguments(task.getTasks()));
isBuilding = true;
listener.prepare();
}
}
public void assembleDebug(boolean installApk) {
execTask(((AssembleDebug) BaseGradleTasks.ASSEMBLE_DEBUG).setInstallApk(installApk));
}
public void assembleRelease() {
execTask(BaseGradleTasks.ASSEMBLE_RELEASE);
}
public void build() {
execTask(BaseGradleTasks.BUILD);
}
public void bundle() {
execTask(BaseGradleTasks.BUNDLE);
}
public void mergeLibAndProjectDex() {
execTask(BaseGradleTasks.MERGE_LIB_AND_PROJECT_DEX);
}
public void mergeExtDex() {
execTask(BaseGradleTasks.MERGE_EXT_DEX);
}
public void mergeDex() {
execTask(BaseGradleTasks.MERGE_DEX);
}
public void clean() {
execTask(BaseGradleTasks.CLEAN);
}
public void cleanAndRebuild() {
execTask(BaseGradleTasks.CLEAN_BUILD);
}
public void stopAllDaemons() {
app.newShell(null).bgAppend("gradle --stop");
}
public void lint() {
execTask(BaseGradleTasks.LINT);
}
public void lintDebug() {
execTask(BaseGradleTasks.LINT_DEBUG);
}
public void lintRelease() {
execTask(BaseGradleTasks.LINT_RELEASE);
}
public boolean isRunning() {
return isRunning;
}
public boolean isBuilding() {
return isBuilding;
}
public boolean isConnected() {
return isConnected;
listener.prepare();
}
}
public boolean isProjectInitialized() {
return projectInitialized;
}
protected void writeMessage(String method, Object params) {
Message msg = new Message();
msg.id = UNIVERSAL_ID++;
msg.method = method;
msg.param = GSON.toJsonTree(params);
write(msg);
}
public static interface BuildListener {
void onProjectInitialized(IDEProject project, Module main);
void onConnectionProgressChanged(String status);
void onBuildProgress(GradleTask task, BuildProgressEvent event);
void onBuildSuccessful(GradleTask task, String msg);
void onBuildFailed(GradleTask task, String msg);
void saveFiles();
void stdOut(GradleTask task, CharSequence text);
void stdErr(GradleTask task, CharSequence text);
void stdSuccess(GradleTask task, CharSequence text);
void prepare();
public void showDependencies() {
execTask(BaseGradleTasks.SHOW_DEPENDENCIES);
}
public void assembleDebug(boolean installApk) {
execTask(((AssembleDebug) BaseGradleTasks.ASSEMBLE_DEBUG).setInstallApk(installApk));
}
public void assembleRelease() {
execTask(BaseGradleTasks.ASSEMBLE_RELEASE);
}
public void build() {
execTask(BaseGradleTasks.BUILD);
}
public void bundle() {
execTask(BaseGradleTasks.BUNDLE);
}
public void mergeLibAndProjectDex() {
execTask(BaseGradleTasks.MERGE_LIB_AND_PROJECT_DEX);
}
public void mergeExtDex() {
execTask(BaseGradleTasks.MERGE_EXT_DEX);
}
public void mergeDex() {
execTask(BaseGradleTasks.MERGE_DEX);
}
public void clean() {
execTask(BaseGradleTasks.CLEAN);
}
public void cleanAndRebuild() {
execTask(BaseGradleTasks.CLEAN_BUILD);
}
public void stopAllDaemons() {
app.newShell(null).bgAppend("gradle --stop");
}
public void lint() {
execTask(BaseGradleTasks.LINT);
}
public void lintDebug() {
execTask(BaseGradleTasks.LINT_DEBUG);
}
public void lintRelease() {
execTask(BaseGradleTasks.LINT_RELEASE);
}
public String typeString(int type) {
switch (type) {
case TASK_SHOW_DEPENDENCIES:
return "";
case TASK_ASSEMBLE_DEBUG:
return getString(R.string.build_debug);
case TASK_ASSEMBLE_RELEASE:
return getString(R.string.build_release);
case TASK_BUILD:
return getString(R.string.build);
case TASK_CLEAN_BUILD:
return getString(R.string.clean_amp_build);
case TASK_CLEAN:
return getString(R.string.clean_project);
case TASK_BUNDLE :
return getString(R.string.create_aab);
case TASK_DEX:
return getString(R.string.compiling);
default: return "";
}
}
public boolean isRunning() {
return isRunning;
}
public boolean isBuilding() {
return isBuilding;
}
public static interface BuildListener {
void onGetDependencies(List<String> dependencies);
void onGetDependenciesFailed();
void onStartingGradleDaemon(GradleTask task);
void onRunTask(GradleTask task, String name);
void onBuildSuccessful(GradleTask task, String msg);
void onBuildFailed(GradleTask task, String msg);
void saveFiles();
void appendOutput(GradleTask task, CharSequence text);
void prepare();
}
}
@@ -21,6 +21,21 @@ public interface GradleTask {
LINT,
HELP,
OTHER
}
}
public static final int TASK_SHOW_DEPENDENCIES = 0;
public static final int TASK_ASSEMBLE_DEBUG = 1;
public static final int TASK_ASSEMBLE_RELEASE = 2;
public static final int TASK_BUILD = 3;
public static final int TASK_BUNDLE = 4;
public static final int TASK_CLEAN = 5;
public static final int TASK_CLEAN_BUILD = 6;
public static final int TASK_COMPILE_JAVA = 7;
public static final int TASK_DEX = 8;
public static final int TASK_LINT = 9;
public static final int TASK_LINT_DEBUG = 10;
public static final int TASK_LINT_RELEASE = 11;
public static final int TASK_TASKS = 12;
}
@@ -7,17 +7,21 @@ import com.itsaky.androidide.tasks.gradle.build.Build;
import com.itsaky.androidide.tasks.gradle.build.Bundle;
import com.itsaky.androidide.tasks.gradle.build.Clean;
import com.itsaky.androidide.tasks.gradle.build.CleanBuild;
import com.itsaky.androidide.tasks.gradle.core.Tasks;
import com.itsaky.androidide.tasks.gradle.dex.MergeDex;
import com.itsaky.androidide.tasks.gradle.dex.MergeExtDex;
import com.itsaky.androidide.tasks.gradle.dex.MergeLibAndProjectDex;
import com.itsaky.androidide.tasks.gradle.lint.Lint;
import com.itsaky.androidide.tasks.gradle.lint.LintDebug;
import com.itsaky.androidide.tasks.gradle.lint.LintRelease;
import com.itsaky.androidide.tasks.gradle.plugin.PrintDependencies;
public class BaseGradleTasks {
public static BaseGradleTask
TASKS = new Tasks(),
BUILD = new Build(),
ASSEMBLE_DEBUG = new AssembleDebug(),
ASSEMBLE_RELEASE = new AssembleRelease(),
@@ -30,5 +34,7 @@ public class BaseGradleTasks {
MERGE_DEX = new MergeDex(),
MERGE_EXT_DEX = new MergeExtDex(),
MERGE_LIB_AND_PROJECT_DEX = new MergeLibAndProjectDex();
MERGE_LIB_AND_PROJECT_DEX = new MergeLibAndProjectDex(),
SHOW_DEPENDENCIES = new PrintDependencies();
}
@@ -0,0 +1,50 @@
package com.itsaky.androidide.tasks.gradle.core;
import com.itsaky.androidide.tasks.BaseGradleTask;
import com.itsaky.androidide.tasks.GradleTask;
import java.io.File;
import java.util.List;
import java.util.Arrays;
public class Tasks extends BaseGradleTask {
@Override
public String getName() {
return "Show tasks";
}
@Override
public String getCommands() {
return "tasks";
}
@Override
public List<String> getTasks() {
return Arrays.asList(getCommands());
}
@Override
public int getTaskID() {
return GradleTask.TASK_TASKS;
}
@Override
public boolean canOutput() {
return true;
}
@Override
public boolean buildsApk() {
return false;
}
@Override
public File getApk(String buildFolder, String moduleName) {
return null;
}
@Override
public GradleTask.Type getType() {
return Type.HELP;
}
}
@@ -0,0 +1,57 @@
package com.itsaky.androidide.tasks.gradle.plugin;
import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.BaseGradleTask;
import java.io.File;
import java.util.List;
import java.util.ArrayList;
public class PrintDependencies extends BaseGradleTask {
@Override
public List<String> getTasks() {
List<String> tasks = new ArrayList<>();
for(String task : getCommands().split("\\s")) {
if(task != null && task.trim().length() > 0)
tasks.add(task);
}
return tasks;
}
@Override
public String getName() {
return "Start Completion Services";
}
@Override
public String getCommands() {
return (StudioApp.getInstance().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true) ? "assembleDebug " : "") + "printDependenciesDebug";
}
@Override
public int getTaskID() {
return IDEService.TASK_SHOW_DEPENDENCIES;
}
@Override
public boolean canOutput() {
return true;
}
@Override
public boolean buildsApk() {
return false;
}
@Override
public File getApk(String buildFolder, String moduleName) {
return null;
}
@Override
public Type getType() {
return Type.ANDROIDIDE_PLUGIN;
}
}
@@ -26,8 +26,7 @@ public final class Environment {
public static final File IDEPROPS;
public static final File INIT_SCRIPT;
public static final File GRADLE_API_DIR;
public static final File GRADLE_API_LIBS_DIR;
public static final File PROJECT_DATA_FILE;
public static final File GRADLE_HOME;
public static final File GRADLE_PROPS;
@@ -61,8 +60,7 @@ public final class Environment {
LIBDIR = mkdirIfNotExits(new File(SYSROOT, "lib"));
IDEPROPS = new File(SYSROOT, "etc/ide-environment.properties");
GRADLE_API_DIR = mkdirIfNotExits(new File(app.getIDEDataDir(), "gradle-api"));
GRADLE_API_LIBS_DIR = new File(GRADLE_API_DIR, "libs");
PROJECT_DATA_FILE = new File(TMP_DIR, "ide_project");
INIT_SCRIPT = new File(mkdirIfNotExits(new File(app.getIDEDataDir(), "init")), "androidide.init.gradle");
BOOTCLASSPATH = new File("");
+1 -1
View File
@@ -230,7 +230,7 @@
<string name="msg_storage_denied">Can\'t proceed without storage permissions</string>
<string name="msg_storage_granted">Storage permissions granted!</string>
<string name="msg_sync_needed">Gradle files have changed. Please rebuild your project to sync dependencies and configurations.</string>
<string name="msg_task_begin">\n\n========== [%s] TASK_BEGIN: %s ==========</string>
<string name="msg_task_begin">========== [%s] TASK_BEGIN: %s ==========</string>
<string name="preparing">Preparing</string>
<string name="preparing_first">Preparing. First build may take up to 10&#8211;15 minutes!</string>
<string name="project_with_package">%1$s (%2$s)</string>