mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Removed usage of 'printDependencies' task
Added 'initializeIDEProject' task in every module project which writes information about the whole project to a file
This commit is contained in:
@@ -119,6 +119,7 @@ import java.util.Stack;
|
||||
import java.util.regex.Pattern;
|
||||
import me.piruin.quickaction.ActionItem;
|
||||
import me.piruin.quickaction.QuickAction;
|
||||
import com.itsaky.androidide.models.project.IDEProject;
|
||||
|
||||
public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener,
|
||||
IDEService.BuildListener,
|
||||
@@ -292,7 +293,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
}
|
||||
|
||||
private void startServices() {
|
||||
getBuildService().showDependencies();
|
||||
getBuildService().initProject();
|
||||
}
|
||||
|
||||
private void removeFromParent(View v) {
|
||||
@@ -702,13 +703,18 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
|
||||
.setPositiveButton(android.R.string.yes, (p1, p2) -> {
|
||||
p1.dismiss();
|
||||
if(getBuildService() != null)
|
||||
getBuildService().showDependencies();
|
||||
getBuildService().initProject();
|
||||
})
|
||||
.setTitle(R.string.failed)
|
||||
.setMessage(R.string.msg_first_prepare_failed)
|
||||
.create().show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProjectLoaded(IDEProject project) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveFiles() {
|
||||
saveAll();
|
||||
|
||||
@@ -34,8 +34,7 @@ public class SplashActivity extends StudioActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
binding.splashText.setText(getString(R.string.msg_checking_storage_permissions));
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ import net.lingala.zip4j.ZipFile;
|
||||
public class ToolsManager {
|
||||
|
||||
private static PreferenceManager prefs;
|
||||
|
||||
|
||||
public static final int JLS_VERSION = 1;
|
||||
public static final int LOGSENDER_VERSION = 1;
|
||||
public static final int CLEANER_VERSION = 1;
|
||||
@@ -37,10 +37,16 @@ public class ToolsManager {
|
||||
writeInitScript();
|
||||
writeBashrc();
|
||||
|
||||
rewriteProjectData();
|
||||
|
||||
if(onFinish != null)
|
||||
onFinish.run();
|
||||
}
|
||||
|
||||
private static void rewriteProjectData() {
|
||||
FileIOUtils.writeFileFromString(Environment.PROJECT_DATA_FILE, "/**********************/");
|
||||
}
|
||||
|
||||
private static void copyBusyboxIfNeeded() {
|
||||
File exec = Environment.BUSYBOX;
|
||||
if(exec.exists()) return;
|
||||
@@ -111,7 +117,7 @@ public class ToolsManager {
|
||||
"PS1=\'\\[\\e[0;34m\\]\\w\\[\\e[0m\\] \\[\\e[0;97m\\]$\\[\\e[0m\\] \'";
|
||||
|
||||
private static final String INIT_SCRIPT =
|
||||
"import org.gradle.api.Plugin"
|
||||
( "import org.gradle.api.Plugin"
|
||||
+ "\nimport org.gradle.api.Project"
|
||||
+ "\nimport groovy.json.*"
|
||||
+ "\n"
|
||||
@@ -123,117 +129,6 @@ public class ToolsManager {
|
||||
+ "\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 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\""
|
||||
@@ -258,7 +153,7 @@ public class ToolsManager {
|
||||
+ "\n "
|
||||
+ "\n project.configurations.matching { it.name == \'_internal_aapt2_binary\' }.all { config ->"
|
||||
+ "\n config.resolutionStrategy.eachDependency { details ->"
|
||||
+ "\n details.useVersion(\"$AAPT2_VERSION\")"
|
||||
+ "\n details.useVersion(\"${AAPT2_VERSION}\")"
|
||||
+ "\n }"
|
||||
+ "\n }"
|
||||
+ "\n "
|
||||
@@ -272,8 +167,49 @@ public class ToolsManager {
|
||||
+ "\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 dependsOn assembleDebug"
|
||||
+ "\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 "
|
||||
+ "\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 out << new JsonBuilder( ideProject ).toPrettyString()"
|
||||
+ "\n "
|
||||
+ "\n println \">>> PROJECT INITIALIZED <<<\""
|
||||
+ "\n }"
|
||||
+ "\n }"
|
||||
+ "\n "
|
||||
+ "\n project.afterEvaluate {"
|
||||
+ "\n project.tasks.getByName(\'assembleDebug\').finalizedBy(\'initializeIDEProject\')"
|
||||
+ "\n project.tasks.getByName(\'assembleRelease\').finalizedBy(\'initializeIDEProject\')"
|
||||
+ "\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 "
|
||||
+ "\n def variantName = variant.name.capitalize()"
|
||||
+ "\n project.tasks.create(name: \"printDependencies${variantName}\", group: \"AndroidIDE\", description: \"List all dependencies in project including those in subprojects for ${variant.name.capitalize()} build.\") {"
|
||||
+ "\n doLast {"
|
||||
+ "\n printDependencies(project, variant)"
|
||||
+ "\n }"
|
||||
@@ -281,6 +217,95 @@ public class ToolsManager {
|
||||
+ "\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 "
|
||||
+ "\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 def printDependencies(def project, def variant) {"
|
||||
+ "\n println \">> DEPENDENCIES START <<\""
|
||||
+ "\n variant.getCompileClasspath().each { dep ->"
|
||||
@@ -324,7 +349,7 @@ public class ToolsManager {
|
||||
+ "\n def description = \"Not defined\""
|
||||
+ "\n def group = \"Not defined\""
|
||||
+ "\n def path = \"Not defined\""
|
||||
+ "\n}";
|
||||
+ "\n}").replace("@@PROJECT_DATA_FILE@@", Environment.PROJECT_DATA_FILE.getAbsolutePath());
|
||||
|
||||
private static final Logger LOG = Logger.instance("ToolsManager");
|
||||
}
|
||||
|
||||
@@ -12,27 +12,27 @@ public class IDEProject {
|
||||
* Name of this project
|
||||
*/
|
||||
@SerializedName("name")
|
||||
public String name;
|
||||
public String name = "";
|
||||
|
||||
/**
|
||||
* Name of this project which should be displayed to user
|
||||
*/
|
||||
@SerializedName("displayName")
|
||||
public String displayName;
|
||||
public String displayName = "";
|
||||
|
||||
/**
|
||||
* Description of this project. Maybe null.
|
||||
*/
|
||||
@SerializedName("description")
|
||||
public String description;
|
||||
public String description = "";
|
||||
|
||||
/**
|
||||
* Path of this project. This is NOT the directory path of this project.
|
||||
*
|
||||
* For example, ':app: or ':module:subModule'
|
||||
* For example, ':app or ':module:subModule'
|
||||
*/
|
||||
@SerializedName("path")
|
||||
public String path;
|
||||
public String path = "";
|
||||
|
||||
/**
|
||||
* Subprojects of this project
|
||||
|
||||
@@ -21,6 +21,15 @@ import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import org.json.JSONObject;
|
||||
import com.itsaky.androidide.models.project.IDEProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.ReadFileTask;
|
||||
import com.itsaky.androidide.utils.RuntimeTypeAdapterFactory;
|
||||
import com.itsaky.androidide.models.project.IDEModule;
|
||||
import com.itsaky.androidide.models.project.IDEAppModule;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.itsaky.androidide.utils.Logger;
|
||||
|
||||
public class IDEService implements ShellServer.Callback {
|
||||
|
||||
@@ -29,21 +38,19 @@ public class IDEService implements ShellServer.Callback {
|
||||
private GradleTask currentTask;
|
||||
private StudioApp app;
|
||||
|
||||
private IDEProject mIDEProject;
|
||||
private AndroidProject project;
|
||||
|
||||
private List<String> dependencies;
|
||||
|
||||
private boolean isBuilding = false;
|
||||
private boolean isRunning = false;
|
||||
private boolean outputtingJars = false;
|
||||
private boolean recreateShellOnDone = false;
|
||||
|
||||
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 = GradleTask.TASK_SHOW_DEPENDENCIES;
|
||||
@@ -59,13 +66,15 @@ public class IDEService implements ShellServer.Callback {
|
||||
public static final int TASK_LINT_DEBUG = GradleTask.TASK_LINT_DEBUG;
|
||||
public static final int TASK_LINT_RELEASE = GradleTask.TASK_LINT_RELEASE;
|
||||
|
||||
public static final Logger LOG = Logger.instance("IDEService");
|
||||
|
||||
public IDEService(AndroidProject project) {
|
||||
this.app = StudioApp.getInstance();
|
||||
this.project = project;
|
||||
|
||||
this.shell = app.newShell(this);
|
||||
|
||||
isRunning = true;
|
||||
this.isRunning = true;
|
||||
}
|
||||
|
||||
public IDEService setListener(BuildListener listener) {
|
||||
@@ -87,42 +96,7 @@ public class IDEService implements ShellServer.Callback {
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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");
|
||||
|
||||
dependencies.add(jar);
|
||||
}
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
|
||||
if(shouldOutput) {
|
||||
String text = line.replace(StudioApp.getInstance().getRootDir().getAbsolutePath(), "ANDROIDIDE_HOME");
|
||||
listener.appendOutput(currentTask, text);
|
||||
@@ -135,7 +109,6 @@ public class IDEService implements ShellServer.Callback {
|
||||
} else
|
||||
if(charSequence.toString().contains(BUILD_SUCCESS)) {
|
||||
isBuilding = false;
|
||||
outputtingJars = false;
|
||||
if(recreateShellOnDone)
|
||||
createShell();
|
||||
if(currentTask != null && currentTask.getTaskID() != TASK_SHOW_DEPENDENCIES)
|
||||
@@ -144,7 +117,6 @@ public class IDEService implements ShellServer.Callback {
|
||||
} else
|
||||
if(charSequence.toString().contains(BUILD_FAILED)) {
|
||||
isBuilding = false;
|
||||
outputtingJars = false;
|
||||
if(recreateShellOnDone)
|
||||
createShell();
|
||||
if(currentTask != null) {
|
||||
@@ -159,6 +131,27 @@ public class IDEService implements ShellServer.Callback {
|
||||
}
|
||||
|
||||
private void readIdeProject() {
|
||||
// Parsing project data is meaningless if there is no one listening...
|
||||
if(listener == null)
|
||||
return;
|
||||
|
||||
final BuildListener local = listener;
|
||||
new Thread(() -> {
|
||||
RuntimeTypeAdapterFactory<IDEProject> typeAdapter = RuntimeTypeAdapterFactory.of(IDEProject.class)
|
||||
.registerSubtype(IDEProject.class, "ide_project")
|
||||
.registerSubtype(IDEModule.class, "ide_module")
|
||||
.registerSubtype(IDEAppModule.class, "ide_app_module");
|
||||
|
||||
IDEProject ideProject = new GsonBuilder()
|
||||
.registerTypeAdapterFactory(typeAdapter)
|
||||
.create()
|
||||
.fromJson(
|
||||
FileIOUtils.readFile2String(Environment.PROJECT_DATA_FILE),
|
||||
IDEProject.class
|
||||
);
|
||||
if(local != null) local.onProjectLoaded(ideProject);
|
||||
LOG.i("Deserialized project: " + ideProject, (ideProject != null ? String.format("Name: %s\nPath: %s\nTask count: %d\nSubproject count:%s", ideProject.name, ideProject.path, ideProject.tasks.size(), ideProject.modules.size()) : ""));
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void appendOutputSeparator() {
|
||||
@@ -256,8 +249,8 @@ public class IDEService implements ShellServer.Callback {
|
||||
}
|
||||
}
|
||||
|
||||
public void showDependencies() {
|
||||
execTask(BaseGradleTasks.SHOW_DEPENDENCIES);
|
||||
public void initProject() {
|
||||
execTask(BaseGradleTasks.INITIALIZE_IDE_PROJECT);
|
||||
}
|
||||
|
||||
public void assembleDebug(boolean installApk) {
|
||||
@@ -343,6 +336,7 @@ public class IDEService implements ShellServer.Callback {
|
||||
}
|
||||
|
||||
public static interface BuildListener {
|
||||
void onProjectLoaded(IDEProject project);
|
||||
void onGetDependencies(List<String> dependencies);
|
||||
void onGetDependenciesFailed();
|
||||
|
||||
|
||||
@@ -37,5 +37,5 @@ public interface GradleTask {
|
||||
public static final int TASK_LINT_DEBUG = 10;
|
||||
public static final int TASK_LINT_RELEASE = 11;
|
||||
public static final int TASK_TASKS = 12;
|
||||
|
||||
public static final int TASK_INIT_PROJECT = 13;
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ 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;
|
||||
import com.itsaky.androidide.tasks.gradle.plugin.InitializeIDEProject;
|
||||
|
||||
public class BaseGradleTasks {
|
||||
|
||||
@@ -36,5 +36,5 @@ public class BaseGradleTasks {
|
||||
MERGE_EXT_DEX = new MergeExtDex(),
|
||||
MERGE_LIB_AND_PROJECT_DEX = new MergeLibAndProjectDex(),
|
||||
|
||||
SHOW_DEPENDENCIES = new PrintDependencies();
|
||||
INITIALIZE_IDE_PROJECT = new InitializeIDEProject();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
package com.itsaky.androidide.tasks.gradle.plugin;
|
||||
|
||||
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 InitializeIDEProject extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "initializeIDEProject";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "initializeIDEProject";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return TASK_INIT_PROJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean buildsApk() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getApk(String buildFolder, String moduleName) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return Type.ANDROIDIDE_PLUGIN;
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
@@ -11,8 +11,8 @@ import com.itsaky.androidide.interfaces.ProjectWriterCallback;
|
||||
import com.itsaky.androidide.models.NewProjectDetails;
|
||||
import com.itsaky.androidide.models.ProjectTemplate;
|
||||
import java.io.File;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ProjectWriter {
|
||||
|
||||
@@ -31,9 +31,7 @@ public class ProjectWriter {
|
||||
MSDK = "$min_sdk",
|
||||
TSDK = "$target_sdk",
|
||||
ANDROIDIDE_PLUGIN = "$androidide_gradle_plugin_version";
|
||||
|
||||
private static final String ANDROIDIDE_PLUGIN_VERSION = "1.0.6";
|
||||
|
||||
|
||||
public static String createMenu() {
|
||||
return "<menu"
|
||||
+ "\n\txmlns:android=\"http://schemas.android.com/apk/res/android\""
|
||||
@@ -129,10 +127,15 @@ public class ProjectWriter {
|
||||
public static String createActivity(String packageName, String className) {
|
||||
return "package " + packageName + ";\n"
|
||||
+ "\n"
|
||||
+ "import android.os.Bundle;"
|
||||
+ "import androidx.appcompat.app.AppCompatActivity;"
|
||||
+ "\n"
|
||||
+ "public class " + className + " extends AppCompatActivity {\n"
|
||||
+ "\t\n"
|
||||
+ " \n"
|
||||
+ "\n @Override"
|
||||
+ "\n protected void onCreate(Bundle savedInstanceState) {"
|
||||
+ "\n super.onCreate(savedInstanceState);"
|
||||
+ "\n }"
|
||||
+ "}";
|
||||
}
|
||||
|
||||
@@ -180,7 +183,7 @@ public class ProjectWriter {
|
||||
.replace(PNAME, details.packageName)
|
||||
.replace(MSDK, String.valueOf(details.minSdk))
|
||||
.replace(TSDK, String.valueOf(details.targetSdk))
|
||||
.replace(ANDROIDIDE_PLUGIN, ANDROIDIDE_PLUGIN_VERSION);
|
||||
.replace(ANDROIDIDE_PLUGIN, "1.0.6");
|
||||
if(FileIOUtils.writeFileFromString(file, read, false)) {
|
||||
continue;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
/*
|
||||
* Copyright (C) 2011 Google Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonObject;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.google.gson.JsonPrimitive;
|
||||
import com.google.gson.TypeAdapter;
|
||||
import com.google.gson.TypeAdapterFactory;
|
||||
import com.google.gson.internal.Streams;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.google.gson.stream.JsonReader;
|
||||
import com.google.gson.stream.JsonWriter;
|
||||
|
||||
/**
|
||||
* Adapts values whose runtime type may differ from their declaration type. This
|
||||
* is necessary when a field's type is not the same type that GSON should create
|
||||
* when deserializing that field. For example, consider these types:
|
||||
* <pre> {@code
|
||||
* abstract class Shape {
|
||||
* int x;
|
||||
* int y;
|
||||
* }
|
||||
* class Circle extends Shape {
|
||||
* int radius;
|
||||
* }
|
||||
* class Rectangle extends Shape {
|
||||
* int width;
|
||||
* int height;
|
||||
* }
|
||||
* class Diamond extends Shape {
|
||||
* int width;
|
||||
* int height;
|
||||
* }
|
||||
* class Drawing {
|
||||
* Shape bottomShape;
|
||||
* Shape topShape;
|
||||
* }
|
||||
* }</pre>
|
||||
* <p>Without additional type information, the serialized JSON is ambiguous. Is
|
||||
* the bottom shape in this drawing a rectangle or a diamond? <pre> {@code
|
||||
* {
|
||||
* "bottomShape": {
|
||||
* "width": 10,
|
||||
* "height": 5,
|
||||
* "x": 0,
|
||||
* "y": 0
|
||||
* },
|
||||
* "topShape": {
|
||||
* "radius": 2,
|
||||
* "x": 4,
|
||||
* "y": 1
|
||||
* }
|
||||
* }}</pre>
|
||||
* This class addresses this problem by adding type information to the
|
||||
* serialized JSON and honoring that type information when the JSON is
|
||||
* deserialized: <pre> {@code
|
||||
* {
|
||||
* "bottomShape": {
|
||||
* "type": "Diamond",
|
||||
* "width": 10,
|
||||
* "height": 5,
|
||||
* "x": 0,
|
||||
* "y": 0
|
||||
* },
|
||||
* "topShape": {
|
||||
* "type": "Circle",
|
||||
* "radius": 2,
|
||||
* "x": 4,
|
||||
* "y": 1
|
||||
* }
|
||||
* }}</pre>
|
||||
* Both the type field name ({@code "type"}) and the type labels ({@code
|
||||
* "Rectangle"}) are configurable.
|
||||
*
|
||||
* <h3>Registering Types</h3>
|
||||
* Create a {@code RuntimeTypeAdapterFactory} by passing the base type and type field
|
||||
* name to the {@link #of} factory method. If you don't supply an explicit type
|
||||
* field name, {@code "type"} will be used. <pre> {@code
|
||||
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory
|
||||
* = RuntimeTypeAdapterFactory.of(Shape.class, "type");
|
||||
* }</pre>
|
||||
* Next register all of your subtypes. Every subtype must be explicitly
|
||||
* registered. This protects your application from injection attacks. If you
|
||||
* don't supply an explicit type label, the type's simple name will be used.
|
||||
* <pre> {@code
|
||||
* shapeAdapterFactory.registerSubtype(Rectangle.class, "Rectangle");
|
||||
* shapeAdapterFactory.registerSubtype(Circle.class, "Circle");
|
||||
* shapeAdapterFactory.registerSubtype(Diamond.class, "Diamond");
|
||||
* }</pre>
|
||||
* Finally, register the type adapter factory in your application's GSON builder:
|
||||
* <pre> {@code
|
||||
* Gson gson = new GsonBuilder()
|
||||
* .registerTypeAdapterFactory(shapeAdapterFactory)
|
||||
* .create();
|
||||
* }</pre>
|
||||
* Like {@code GsonBuilder}, this API supports chaining: <pre> {@code
|
||||
* RuntimeTypeAdapterFactory<Shape> shapeAdapterFactory = RuntimeTypeAdapterFactory.of(Shape.class)
|
||||
* .registerSubtype(Rectangle.class)
|
||||
* .registerSubtype(Circle.class)
|
||||
* .registerSubtype(Diamond.class);
|
||||
* }</pre>
|
||||
*
|
||||
* <h3>Serialization and deserialization</h3>
|
||||
* In order to serialize and deserialize a polymorphic object,
|
||||
* you must specify the base type explicitly.
|
||||
* <pre> {@code
|
||||
* Diamond diamond = new Diamond();
|
||||
* String json = gson.toJson(diamond, Shape.class);
|
||||
* }</pre>
|
||||
* And then:
|
||||
* <pre> {@code
|
||||
* Shape shape = gson.fromJson(json, Shape.class);
|
||||
* }</pre>
|
||||
*/
|
||||
public final class RuntimeTypeAdapterFactory<T> implements TypeAdapterFactory {
|
||||
private final Class<?> baseType;
|
||||
private final String typeFieldName;
|
||||
private final Map<String, Class<?>> labelToSubtype = new LinkedHashMap<String, Class<?>>();
|
||||
private final Map<Class<?>, String> subtypeToLabel = new LinkedHashMap<Class<?>, String>();
|
||||
private final boolean maintainType;
|
||||
|
||||
private RuntimeTypeAdapterFactory(Class<?> baseType, String typeFieldName, boolean maintainType) {
|
||||
if (typeFieldName == null || baseType == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
this.baseType = baseType;
|
||||
this.typeFieldName = typeFieldName;
|
||||
this.maintainType = maintainType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter using for {@code baseType} using {@code
|
||||
* typeFieldName} as the type field name. Type field names are case sensitive.
|
||||
* {@code maintainType} flag decide if the type will be stored in pojo or not.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName, boolean maintainType) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, maintainType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter using for {@code baseType} using {@code
|
||||
* typeFieldName} as the type field name. Type field names are case sensitive.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType, String typeFieldName) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, typeFieldName, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new runtime type adapter for {@code baseType} using {@code "type"} as
|
||||
* the type field name.
|
||||
*/
|
||||
public static <T> RuntimeTypeAdapterFactory<T> of(Class<T> baseType) {
|
||||
return new RuntimeTypeAdapterFactory<T>(baseType, "type", false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@code type} identified by {@code label}. Labels are case
|
||||
* sensitive.
|
||||
*
|
||||
* @throws IllegalArgumentException if either {@code type} or {@code label}
|
||||
* have already been registered on this type adapter.
|
||||
*/
|
||||
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type, String label) {
|
||||
if (type == null || label == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
if (subtypeToLabel.containsKey(type) || labelToSubtype.containsKey(label)) {
|
||||
throw new IllegalArgumentException("types and labels must be unique");
|
||||
}
|
||||
labelToSubtype.put(label, type);
|
||||
subtypeToLabel.put(type, label);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers {@code type} identified by its {@link Class#getSimpleName simple
|
||||
* name}. Labels are case sensitive.
|
||||
*
|
||||
* @throws IllegalArgumentException if either {@code type} or its simple name
|
||||
* have already been registered on this type adapter.
|
||||
*/
|
||||
public RuntimeTypeAdapterFactory<T> registerSubtype(Class<? extends T> type) {
|
||||
return registerSubtype(type, type.getSimpleName());
|
||||
}
|
||||
|
||||
public <R> TypeAdapter<R> create(Gson gson, TypeToken<R> type) {
|
||||
if (type.getRawType() != baseType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, TypeAdapter<?>> labelToDelegate
|
||||
= new LinkedHashMap<String, TypeAdapter<?>>();
|
||||
final Map<Class<?>, TypeAdapter<?>> subtypeToDelegate
|
||||
= new LinkedHashMap<Class<?>, TypeAdapter<?>>();
|
||||
for (Map.Entry<String, Class<?>> entry : labelToSubtype.entrySet()) {
|
||||
TypeAdapter<?> delegate = gson.getDelegateAdapter(this, TypeToken.get(entry.getValue()));
|
||||
labelToDelegate.put(entry.getKey(), delegate);
|
||||
subtypeToDelegate.put(entry.getValue(), delegate);
|
||||
}
|
||||
|
||||
return new TypeAdapter<R>() {
|
||||
@Override public R read(JsonReader in) throws IOException {
|
||||
JsonElement jsonElement = Streams.parse(in);
|
||||
JsonElement labelJsonElement;
|
||||
if (maintainType) {
|
||||
labelJsonElement = jsonElement.getAsJsonObject().get(typeFieldName);
|
||||
} else {
|
||||
labelJsonElement = jsonElement.getAsJsonObject().remove(typeFieldName);
|
||||
}
|
||||
|
||||
if (labelJsonElement == null) {
|
||||
throw new JsonParseException("cannot deserialize " + baseType
|
||||
+ " because it does not define a field named " + typeFieldName);
|
||||
}
|
||||
String label = labelJsonElement.getAsString();
|
||||
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||
TypeAdapter<R> delegate = (TypeAdapter<R>) labelToDelegate.get(label);
|
||||
if (delegate == null) {
|
||||
throw new JsonParseException("cannot deserialize " + baseType + " subtype named "
|
||||
+ label + "; did you forget to register a subtype?");
|
||||
}
|
||||
return delegate.fromJsonTree(jsonElement);
|
||||
}
|
||||
|
||||
@Override public void write(JsonWriter out, R value) throws IOException {
|
||||
Class<?> srcType = value.getClass();
|
||||
String label = subtypeToLabel.get(srcType);
|
||||
@SuppressWarnings("unchecked") // registration requires that subtype extends T
|
||||
TypeAdapter<R> delegate = (TypeAdapter<R>) subtypeToDelegate.get(srcType);
|
||||
if (delegate == null) {
|
||||
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||
+ "; did you forget to register a subtype?");
|
||||
}
|
||||
JsonObject jsonObject = delegate.toJsonTree(value).getAsJsonObject();
|
||||
|
||||
if (maintainType) {
|
||||
Streams.write(jsonObject, out);
|
||||
return;
|
||||
}
|
||||
|
||||
JsonObject clone = new JsonObject();
|
||||
|
||||
if (jsonObject.has(typeFieldName)) {
|
||||
throw new JsonParseException("cannot serialize " + srcType.getName()
|
||||
+ " because it already defines a field named " + typeFieldName);
|
||||
}
|
||||
clone.add(typeFieldName, new JsonPrimitive(label));
|
||||
|
||||
for (Map.Entry<String, JsonElement> e : jsonObject.entrySet()) {
|
||||
clone.add(e.getKey(), e.getValue());
|
||||
}
|
||||
Streams.write(clone, out);
|
||||
}
|
||||
}.nullSafe();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user