diff --git a/app/libs/api-messages.jar b/app/libs/api-messages.jar
new file mode 100644
index 000000000..0265c5a40
Binary files /dev/null and b/app/libs/api-messages.jar differ
diff --git a/app/libs/api-model.jar b/app/libs/api-model.jar
new file mode 100644
index 000000000..2234c4539
Binary files /dev/null and b/app/libs/api-model.jar differ
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index cc20f27db..d17e89651 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -49,10 +49,6 @@
android:name=".AboutActivity"
android:configChanges="orientation|screenSize|uiMode">
-
-
diff --git a/app/src/main/assets/data/bin.zip b/app/src/main/assets/data/bin.zip
index 3b34afa97..06cafc063 100644
Binary files a/app/src/main/assets/data/bin.zip and b/app/src/main/assets/data/bin.zip differ
diff --git a/app/src/main/assets/data/gradle-api.zip b/app/src/main/assets/data/gradle-api.zip
new file mode 100644
index 000000000..f9669c54a
Binary files /dev/null and b/app/src/main/assets/data/gradle-api.zip differ
diff --git a/app/src/main/assets/data/jls.zip b/app/src/main/assets/data/jls.zip
index 9a01eb471..7a76fa90e 100644
Binary files a/app/src/main/assets/data/jls.zip and b/app/src/main/assets/data/jls.zip differ
diff --git a/app/src/main/assets/data/lib/libc++_shared.so b/app/src/main/assets/data/lib/libc++_shared.so
deleted file mode 100644
index 44ceeb217..000000000
Binary files a/app/src/main/assets/data/lib/libc++_shared.so and /dev/null differ
diff --git a/app/src/main/assets/data/lib/libgcc.so b/app/src/main/assets/data/lib/libgcc.so
deleted file mode 100644
index b3ecce967..000000000
Binary files a/app/src/main/assets/data/lib/libgcc.so and /dev/null differ
diff --git a/app/src/main/assets/data/lib/libgcc_real.so b/app/src/main/assets/data/lib/libgcc_real.so
deleted file mode 100644
index d96dc99d3..000000000
Binary files a/app/src/main/assets/data/lib/libgcc_real.so and /dev/null differ
diff --git a/app/src/main/assets/data/lib/libhook.so b/app/src/main/assets/data/lib/libhook.so
deleted file mode 100644
index 23ead2db5..000000000
Binary files a/app/src/main/assets/data/lib/libhook.so and /dev/null differ
diff --git a/app/src/main/assets/data/lib/libpthread.so b/app/src/main/assets/data/lib/libpthread.so
deleted file mode 100644
index 98a0f7ba7..000000000
Binary files a/app/src/main/assets/data/lib/libpthread.so and /dev/null differ
diff --git a/app/src/main/assets/data/lib/librt.so b/app/src/main/assets/data/lib/librt.so
deleted file mode 100644
index 98a0f7ba7..000000000
Binary files a/app/src/main/assets/data/lib/librt.so and /dev/null differ
diff --git a/app/src/main/assets/data/lib/libz.so.1 b/app/src/main/assets/data/lib/libz.so.1
deleted file mode 100644
index 368b545c1..000000000
Binary files a/app/src/main/assets/data/lib/libz.so.1 and /dev/null differ
diff --git a/app/src/main/java/com/itsaky/androidide/DownloadActivity.java b/app/src/main/java/com/itsaky/androidide/DownloadActivity.java
index 43a9477bf..5bc505539 100644
--- a/app/src/main/java/com/itsaky/androidide/DownloadActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/DownloadActivity.java
@@ -15,11 +15,11 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityDownloadBinding;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
+import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.ListDirectoryCallable;
import com.itsaky.androidide.utils.FileUtil;
-import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.io.FileFilter;
@@ -84,7 +84,7 @@ public class DownloadActivity extends StudioActivity {
}
}
- private final String DONE = "'Installed successfully.'";
+ private final String DONE = "DONE";
private void installAll() {
showProgress();
getApp().getPrefManager().putBoolean(PreferenceManager.KEY_FRAMEWORK_DOWNLOADED, true);
diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
index 7e70aa60b..c092b86ac 100644
--- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java
@@ -1,11 +1,8 @@
package com.itsaky.androidide;
import android.annotation.SuppressLint;
-import android.content.BroadcastReceiver;
-import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
@@ -39,9 +36,9 @@ import com.blankj.utilcode.util.ThrowableUtils;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.navigation.NavigationView;
import com.google.android.material.tabs.TabLayout;
-import com.google.android.material.transition.MaterialArcMotion;
import com.google.android.material.transition.MaterialContainerTransform;
import com.google.gson.Gson;
+import com.google.gson.GsonBuilder;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import com.itsaky.androidide.adapters.DiagnosticsAdapter;
@@ -60,13 +57,17 @@ import com.itsaky.androidide.fragments.sheets.ProgressSheet;
import com.itsaky.androidide.fragments.sheets.TextSheetFragment;
import com.itsaky.androidide.interfaces.DiagnosticClickListener;
import com.itsaky.androidide.interfaces.JLSRequestor;
+import com.itsaky.androidide.language.buildout.BuildOutputLanguage;
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.androidide.language.logs.LogLanguageImpl;
+import com.itsaky.androidide.managers.PreferenceManager;
+import com.itsaky.androidide.managers.VersionedFileManager;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.DiagnosticGroup;
import com.itsaky.androidide.models.LogLine;
import com.itsaky.androidide.models.SearchResult;
import com.itsaky.androidide.models.SheetOption;
+import com.itsaky.androidide.models.build.OperationHierarchyNode;
import com.itsaky.androidide.receivers.LogReceiver;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.shell.ShellServer;
@@ -75,14 +76,19 @@ import com.itsaky.androidide.tasks.GradleTask;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.Logger;
-import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.ProjectWriter;
import com.itsaky.androidide.utils.RecursiveFileSearcher;
import com.itsaky.androidide.utils.Symbols;
+import com.itsaky.androidide.utils.TransformUtils;
import com.itsaky.androidide.utils.TypefaceUtils;
-import com.itsaky.androidide.utils.VersionedFileManager;
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;
@@ -109,7 +115,6 @@ import com.itsaky.lsp.TextDocumentItem;
import com.itsaky.lsp.TextDocumentPositionParams;
import com.itsaky.toaster.Toaster;
import com.unnamed.b.atv.model.TreeNode;
-import io.github.rosemoe.editor.langs.EmptyLanguage;
import io.github.rosemoe.editor.text.Content;
import io.github.rosemoe.editor.widget.CodeEditor;
import java.io.File;
@@ -151,9 +156,8 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private OptionsListFragment mFileOptionsFragment;
private ProgressSheet mSearchingProgress;
private AlertDialog mFindInProjectDialog;
-
- private boolean recreateCompletionServices = false;
+ private final List operationTree = new ArrayList<>();
private final Map> diagnostics = new HashMap<>();
private final Stack pendingMessages = new Stack<>();
@@ -164,7 +168,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private final String JAVA_PATH_REGEX = "/.*/src/.*/java";
private static final Gson gson = new Gson();
- private final IDEService.BuildListener mBuildListener = this;
private static final String TAG_FILE_OPTIONS_FRAGMENT = "file_options_fragment";
public static final String TAG = "EditorActivity";
@@ -172,19 +175,13 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
private static final int ACTIONID_OTHERS = 101;
private static final int ACTIONID_ALL = 102;
+ private static final int STD_TYPE_NORMAL = 0;
+ private static final int STD_TYPE_ERROR = 1;
+ private static final int STD_TYPE_SUCCESS = 2;
+
+ public static final String EXTRA_PROJECT = "project";
+
private LogReceiver mLogReceiver = new LogReceiver().setLogListener(line -> appendApkLog(line));
- private BroadcastReceiver mServiceStartReceiver = new BroadcastReceiver(){
-
- @Override
- public void onReceive(Context p1, Intent p2) {
- unregisterReceiver(this);
- getApp().storeBuildServiceInstance(mBuildListener);
- if (getApp().isStopGradleDaemon() && getBuildService() != null) getBuildService().stopAllDaemons();
- getBuildService().setModule(mProject.getMainModulePath());
- preBuild();
- invalidateOptionsMenu();
- }
- };
/**
* MenuItem(s) that are related to the build process
@@ -216,7 +213,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
super.onCreate(savedInstanceState);
setSupportActionBar(mBinding.editorToolbar);
- this.mProject = getIntent().getParcelableExtra("project");
+ getProjectFromIntent();
mPagerAdapter = new EditorPagerAdapter(getSupportFragmentManager(), this.mProject);
mFileTreeFragment = FileTreeFragment.newInstance(this.mProject).setFileActionListener(this);
mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true);
@@ -238,23 +235,20 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
mBinding.fabView.setOnClickListener(v -> showViewOptions());
getApp().checkAndUpdateGradle();
- if (getBuildService() == null) {
- registerServiceReceiver();
- getApp().startBuildService();
- } else {
- if (getApp().isStopGradleDaemon() && getBuildService() != null) getBuildService().stopAllDaemons();
- getBuildService().setModule(mProject.getMainModulePath());
- getBuildService().setListener(this);
- preBuild();
- invalidateOptionsMenu();
- }
-
+ getApp().startBuildService(new File(mProject.getProjectPath()));
+ getApp().getBuildService().setListener(this);
+
KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged());
registerLogReceiver();
setupContainers();
setupSignatureText();
}
+ private void getProjectFromIntent() {
+ this.mProject = getIntent().getParcelableExtra(EXTRA_PROJECT);
+ getApp().getPrefManager().setOpenedProject(this.mProject.getProjectPath());
+ }
+
private void setupSignatureText() {
GradientDrawable gd = new GradientDrawable();
gd.setShape(GradientDrawable.RECTANGLE);
@@ -328,7 +322,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
private void notifySyncNeeded() {
- if(getBuildService() != null && !getBuildService().isBuilding()) {
+ if(getBuildService() != null && getBuildService().isProjectInitialized() && !getBuildService().isBuilding()) {
getSyncBanner()
.setNegative(android.R.string.cancel, null)
.setPositive(android.R.string.ok, v -> {
@@ -337,6 +331,35 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
.show();
}
}
+
+ private void closeProject(boolean manualFinish) {
+ if (getBuildService() != null && getBuildService().isProjectInitialized()) {
+ getBuildService().setListener(null);
+ getBuildService().exit();
+ }
+
+ getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT);
+
+ if(getApp().getJavaLanguageServer() != null)
+ getApp().getJavaLanguageServer().exit();
+
+ getApp().stopAllDaemons();
+
+ if(manualFinish)
+ finish();
+ }
+
+ private void confirmProjectClose() {
+ final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog);
+ builder.setTitle(R.string.title_confirm_project_close);
+ builder.setMessage(R.string.msg_confirm_project_close);
+ builder.setNegativeButton(android.R.string.no, null);
+ builder.setPositiveButton(android.R.string.yes, (d, w) -> {
+ d.dismiss();
+ closeProject(true);
+ });
+ builder.show();
+ }
@Override
public void onBackPressed() {
@@ -359,7 +382,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
} else if(mFileOptionsFragment != null && mFileOptionsFragment.isShowing()) {
mFileOptionsFragment.dismiss();
} else {
- super.onBackPressed();
+ confirmProjectClose();
}
}
@@ -375,14 +398,11 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
@Override
protected void onDestroy() {
- super.onDestroy();
- if (getBuildService() != null) {
- getBuildService().setListener(null);
- }
- try {
- unregisterReceiver(mServiceStartReceiver);
- unregisterReceiver(mLogReceiver);
+ closeProject(false);
+ try {
+ unregisterReceiver(mLogReceiver);
} catch (Throwable th) {}
+ super.onDestroy();
}
@Override
@@ -391,7 +411,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().isBuilding();
+ boolean enabled = getBuildService() != null && getBuildService().isProjectInitialized() && !getBuildService().isBuilding();
item.setEnabled(enabled);
item.getIcon().setAlpha(enabled ? 255 : 76);
}
@@ -604,19 +624,28 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
getLogLanguage().addLine(line);
appendLogOut(line.toString());
}
+
+ @Override
+ public void stdOut(GradleTask task, CharSequence text) {
+ if(text == null) text = "";
+ appendBuildOut(text.toString(), STD_TYPE_NORMAL);
+ }
@Override
- public void appendOutput(GradleTask task, CharSequence text) {
- if(text == null)
- return;
- if(task != null && !task.canOutput()) {
- return;
- }
- appendBuildOut(text.toString());
+ 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);
+ }
+
@Override
- public void showPreparing() {
+ public void prepare() {
+ operationTree.clear();
boolean isFirstBuild = getApp().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true);
setStatus(getString(isFirstBuild ? R.string.preparing_first : R.string.preparing));
if(isFirstBuild) {
@@ -625,20 +654,106 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
@Override
- public void onStartingGradleDaemon(GradleTask task) {
- setStatus(getString(R.string.msg_starting_daemon));
- getApp().setStopGradleDaemon(false);
+ public void onConnectionProgressChanged(String status) {
+ setStatus(status);
}
- @Override
- public void onRunTask(String taskName, GradleTask task) {
- if(task != null && task.canOutput()) {
- setStatus(getBuildService().typeString(task.getTaskID()) + " " + taskName);
- }
- }
+ @Override
+ public void onBuildProgress(GradleTask gradleTask, BuildProgressEvent event) {
+
+ OperationDescriptor descriptor = event.descriptor;
+ OperationDescriptor parent = descriptor.parent;
+
+ OperationHierarchyNode node = createNode(event);
+ if(parent == null) {
+ // This is a root node, add it at level 0 in tree
+ if(operationTree.contains(node)) {
+ operationTree.set(operationTree.indexOf(node), node);
+ } else operationTree.add(node);
+ } else {
+ // A nested node must be added in the appropriate parent node
+
+ // We first get the list of parents
+ // Last item in this list will always be the direct parent of current node
+ final List parents = new ArrayList<>();
+ while(parent != null) {
+ parents.add(0, parent);
+ parent = parent.parent;
+ }
+
+ List list = new ArrayList(operationTree);
+ OperationHierarchyNode direct = null;
+ final int last = parents.size() - 1;
+ for(int i=0;i(direct.children);
+ }
+ }
+ }
+
+ 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));
+ }
+ }
+ }
+
+ private OperationHierarchyNode findNodeByLabelInList(String label, List list) {
+ if(list == null || label == null) return null;
+ for(int i=0;i dependencies) {
- setStatus(getString(R.string.msg_starting_completion));
- mProject.setClassPaths(dependencies);
+ @Override
+ public void onProjectInitialized(IDEProject project, Module main) {
+ invalidateOptionsMenu();
+ setStatus(getString(R.string.msg_starting_completion));
+ mProject.setClassPaths(new ArrayList(main.dependencyJars));
createServices();
- }
-
- @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() {
@@ -697,10 +798,15 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
// Can be called from a different different, better to run on UI thread
- private void appendBuildOut(final String str) {
+ private void appendBuildOut(final String str, int type) {
runOnUiThread(() -> {
String strFinal = str.endsWith("\n") ? str : str.concat("\n");
- getBuildView(true).getText().append(strFinal);
+ 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);
+ }
});
}
@@ -822,20 +928,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
}
private MaterialContainerTransform createContainerTransformFor(View start, View end, View drawingView) {
- MaterialContainerTransform transform = new MaterialContainerTransform();
- transform.setStartView(start);
- transform.setEndView(end);
- transform.addTarget(end);
- transform.setDrawingViewId(drawingView.getId());
- transform.setAllContainerColors(ContextCompat.getColor(this, R.color.primaryDarkColor));
- transform.setElevationShadowEnabled(true);
- transform.setPathMotion(new MaterialArcMotion());
- transform.setScrimColor(Color.TRANSPARENT);
- return transform;
- }
-
- private void preBuild() {
- getBuildService().showDependencies();
+ return TransformUtils.createContainerTransformFor(start, end, drawingView);
}
private void install(File apk) {
@@ -855,12 +948,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
setStatus(getString(getApp().areCompletorsStarted() ? R.string.msg_service_started : R.string.msg_starting_completion_failed));
});
}
-
- private void registerServiceReceiver() {
- IntentFilter filter = new IntentFilter();
- filter.addAction(IDEService.ACTION_CREATED);
- registerReceiver(mServiceStartReceiver, filter);
- }
private void registerLogReceiver() {
IntentFilter filter = new IntentFilter();
@@ -1268,7 +1355,10 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
startActivity(new Intent(this, PreferencesActivity.class));
} else if(id == R.id.editornav_share) {
startActivity(IntentUtils.getShareTextIntent(getString(R.string.msg_share_app)));
- }
+ } else if(id == R.id.editornav_close_project) {
+ confirmProjectClose();
+ }
+ mBinding.getRoot().closeDrawer(GravityCompat.START);
return false;
}
@@ -1342,7 +1432,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
buildView = new CodeEditor(this);
buildView.setEditable(false);
buildView.setDividerWidth(0);
- buildView.setEditorLanguage(new EmptyLanguage());
+ buildView.setEditorLanguage(new BuildOutputLanguage());
buildView.setOverScrollEnabled(false);
buildView.setTextActionMode(CodeEditor.TextActionMode.ACTION_MODE);
buildView.setWordwrap(false);
@@ -1852,4 +1942,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F
invalidateOptionsMenu();
}
+
+ private static final Logger LOG = Logger.instance("EditorActivity");
}
diff --git a/app/src/main/java/com/itsaky/androidide/MainActivity.java b/app/src/main/java/com/itsaky/androidide/MainActivity.java
index 660ed7335..f2ae98f3d 100644
--- a/app/src/main/java/com/itsaky/androidide/MainActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/MainActivity.java
@@ -4,100 +4,372 @@ import abhishekti7.unicorn.filepicker.UnicornFilePicker;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
-import com.blankj.utilcode.util.SizeUtils;
-import com.google.android.material.dialog.MaterialAlertDialogBuilder;
-import com.itsaky.androidide.adapters.MainPagerAdapter;
+import androidx.transition.Transition;
+import androidx.transition.TransitionManager;
+import com.google.android.material.transition.MaterialContainerTransform;
import com.itsaky.androidide.app.StudioActivity;
import com.itsaky.androidide.databinding.ActivityMainBinding;
-import com.itsaky.androidide.fragments.MainFragment;
-import com.itsaky.androidide.fragments.ProjectsFragment;
+import com.itsaky.androidide.databinding.LayoutCreateProjectBinding;
+import com.itsaky.androidide.databinding.LayoutCreateProjectContentBinding;
import com.itsaky.androidide.fragments.sheets.ProgressSheet;
-import com.itsaky.androidide.shell.ShellServer;
-import com.itsaky.androidide.utils.FileUtil;
-import com.itsaky.androidide.views.VerticalViewPager;
+import com.itsaky.androidide.interfaces.ProjectWriterCallback;
+import com.itsaky.androidide.managers.PreferenceManager;
+import com.itsaky.androidide.models.AndroidProject;
+import com.itsaky.androidide.models.NewProjectDetails;
+import com.itsaky.androidide.models.ProjectTemplate;
+import com.itsaky.androidide.project.utils.ProjectFinder;
+import com.itsaky.androidide.tasks.TaskExecutor;
+import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable;
+import com.itsaky.androidide.utils.TransformUtils;
import com.itsaky.toaster.Toaster;
import java.io.File;
import java.util.ArrayList;
-import com.itsaky.androidide.utils.Environment;
+import java.util.concurrent.Callable;
+
+public class MainActivity extends StudioActivity implements View.OnClickListener, ProjectWriterCallback {
-public class MainActivity extends StudioActivity
-{
private ActivityMainBinding binding;
-
- private MainFragment mMainFragment;
- private ProjectsFragment mProjectsFragment;
- private MainPagerAdapter mPagerAdapter;
-
- private int currentItem = 0;
-
- public static final String TAG = "MainActivity";
+ private LayoutCreateProjectBinding createBinding;
+ private LayoutCreateProjectContentBinding createLayoutBinding;
+
+ private ProgressSheet mProgressSheet;
+
+ private ArrayList mTemplates = new ArrayList();
+
+ private int currentTemplateIndex = 0;
@Override
protected View bindLayout() {
binding = ActivityMainBinding.inflate(getLayoutInflater());
+ createBinding = binding.createLayout;
+ createLayoutBinding = createBinding.createProjectContent;
return binding.getRoot();
}
-
+
@Override
- protected void onCreate(Bundle savedInstanceState)
- {
+ protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- mPagerAdapter = new MainPagerAdapter(getSupportFragmentManager())
- .addFragment(getMainFragment())
- .addFragment(getProjectsFragment());
- binding.mainViewPager.setAdapter(mPagerAdapter);
- binding.mainMoveDown.hide();
- binding.mainMoveDown.setOnClickListener(v -> showFirst());
- binding.settings.setOnClickListener(v -> gotoSettings());
- binding.mainViewPager.setOnPageChangeListener(new VerticalViewPager.OnPageChangeListener(){
- @Override
- public void onPageScrolled(int p1, float p2, int p3) {
- float elevation = SizeUtils.dp2px(8) * (currentItem == 0 ? p2 : -p2);
- binding.activitymainTextView1.setElevation(elevation);
- }
+ binding.createProject.setOnClickListener(this);
+ binding.openProject.setOnClickListener(this);
+ createBinding.createprojectClose.setOnClickListener(this);
+ createLayoutBinding.createprojectCreate.setOnClickListener(this);
+ createLayoutBinding.previousCard.setOnClickListener(this);
+ createLayoutBinding.nextCard.setOnClickListener(this);
+ binding.createNewCard.setVisibility(View.GONE);
- @Override
- public void onPageSelected(int p1) {
- currentItem = p1;
- if(p1 == 1)
- binding.mainMoveDown.show();
- else binding.mainMoveDown.hide();
- }
+ currentTemplateIndex = 0;
+ createTemplates();
+ createProgressSheet();
+ showTemplate(currentTemplateIndex);
- @Override
- public void onPageScrollStateChanged(int p1) {
- }
- });
+ binding.gotoPreferences.setOnClickListener(this);
+
+ binding.getRoot().post(() -> {
+ PreferenceManager manager = getApp().getPrefManager();
+ if(manager.wasProjectOpened()) {
+ String path = manager.getOpenedProject();
+ if(path == null && path.trim().isEmpty()) {
+ getApp().toast(R.string.msg_opened_project_does_not_exist, Toaster.Type.INFO);
+ } else {
+ File root = new File(manager.getOpenedProject());
+ if(!root.exists()) {
+ getApp().toast(R.string.msg_opened_project_does_not_exist, Toaster.Type.INFO);
+ } else {
+ openProject(root);
+ }
+ }
+ }
+ });
}
-
+
private void gotoSettings() {
startActivity(new Intent(this, PreferencesActivity.class));
}
-
- private void showFirst() {
- if(binding.mainViewPager.getCurrentItem() != 0)
- binding.mainViewPager.setCurrentItem(0, true);
+
+ @Override
+ protected void onStorageGranted() {
}
@Override
- protected void onStorageGranted()
- {
- getProjectsFragment().loadProjects();
- }
-
- @Override
- protected void onStorageDenied()
- {
+ protected void onStorageDenied() {
getApp().toast(R.string.msg_storage_denied, Toaster.Type.ERROR);
finishAffinity();
}
-
- private MainFragment getMainFragment() {
- return mMainFragment == null ? mMainFragment = new MainFragment().setCreateProjectListener(() -> getProjectsFragment().loadProjects()).setOnCreateProjectVisibilityListener((isVisible) -> binding.mainViewPager.setScrollEnabled(!isVisible)) : mMainFragment;
+
+ @Override
+ public void onClick(View p1) {
+ if (p1.getId() == binding.createProject.getId())
+ showCreateProject();
+ else if (p1.getId() == createBinding.createprojectClose.getId())
+ hideCreateProject();
+ else if (p1.getId() == createLayoutBinding.nextCard.getId()) {
+ currentTemplateIndex++;
+ if (currentTemplateIndex >= mTemplates.size()) currentTemplateIndex = 0;
+ showTemplate(currentTemplateIndex);
+ } else if (p1.getId() == createLayoutBinding.previousCard.getId()) {
+ currentTemplateIndex--;
+ if (currentTemplateIndex < 0) currentTemplateIndex = mTemplates.size() - 1;
+ showTemplate(currentTemplateIndex);
+ } else if (p1.getId() == createLayoutBinding.createprojectCreate.getId())
+ createNewProject();
+ else if (p1.getId() == binding.gotoPreferences.getId()) {
+ gotoSettings();
+ } else if (p1.getId() == binding.openProject.getId()) {
+ pickProject();
+ }
}
-
- private ProjectsFragment getProjectsFragment() {
- return mProjectsFragment == null ? mProjectsFragment = new ProjectsFragment() : mProjectsFragment;
+
+ @Override
+ public void beforeBegin() {
+ if (mProgressSheet == null)
+ createProgressSheet();
+
+ setMessage(R.string.msg_begin_project_write);
+ }
+
+ @Override
+ public void onProcessTask(String taskName) {
+ setMessage(taskName);
+ }
+
+ @Override
+ public void onSuccess(File root) {
+ if (mProgressSheet != null)
+ createProgressSheet();
+
+ if (mProgressSheet.isShowing())
+ mProgressSheet.dismiss();
+ getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
+
+ openProject(root);
+ }
+
+ @Override
+ public void onFailed(String reason) {
+ if (mProgressSheet != null)
+ createProgressSheet();
+
+ if (mProgressSheet.isShowing())
+ mProgressSheet.dismiss();
+ getApp().toast(reason, Toaster.Type.ERROR);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ super.onActivityResult(requestCode, resultCode, data);
+ if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE && resultCode == RESULT_OK) {
+ ArrayList files = data.getStringArrayListExtra("filePaths");
+ if (files != null) {
+ if (files.size() == 1) {
+ File choosenDir = new File(files.get(0));
+ if (choosenDir.exists() && choosenDir.isDirectory()) {
+ openProject(choosenDir);
+ } else {
+ getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
+ choosenDir = null;
+ }
+ } else {
+ getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if(binding.createNewCard.getVisibility() == View.VISIBLE) {
+ hideCreateProject();
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ if(mProgressSheet != null
+ && mProgressSheet.isShowing()) {
+ mProgressSheet.dismiss();
+ }
+ }
+
+ private void showTemplate(int index) {
+ if (index < 0 || index >= mTemplates.size()) return;
+ final ProjectTemplate template = mTemplates.get(index);
+ createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
+ createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
+ createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
+ }
+
+ /**
+ * Must be called in/after onViewCreated
+ */
+ private void createTemplates() {
+ mTemplates = new ArrayList<>();
+
+ ProjectTemplate
+ empty = new ProjectTemplate()
+ .setId(0)
+ .setName(this, R.string.template_empty)
+ .setDescription(this, R.string.template_description_empty)
+ .setImageId(R.drawable.template_empty),
+ basic = new ProjectTemplate()
+ .setId(1)
+ .setName(this, R.string.template_basic)
+ .setDescription(this, R.string.template_description_basic)
+ .setImageId(R.drawable.template_basic),
+ drawer = new ProjectTemplate()
+ .setId(2)
+ .setName(this, R.string.template_navigation_drawer)
+ .setDescription(this, R.string.template_description_navigation_drawer)
+ .setImageId(R.drawable.template_navigation_drawer);
+ mTemplates.add(empty);
+ mTemplates.add(basic);
+ mTemplates.add(drawer);
+
+ }
+
+ private void createProgressSheet() {
+ mProgressSheet = new ProgressSheet();
+ }
+
+ private void setMessage(int msg) {
+ setMessage(getString(msg));
+ }
+
+ private void setMessage(String msg) {
+ mProgressSheet.setMessage(msg);
+ }
+
+ private void openProject(File root) {
+ setMessage(R.string.msg_opening_project);
+ mProgressSheet.setCancelable(false);
+ mProgressSheet.show(getSupportFragmentManager(), "opening_project");
+
+ new TaskExecutor().executeAsync(new ProjectReader(root), project -> openEditor(project, false));
+ }
+
+ private void pickProject() {
+ UnicornFilePicker.from(this)
+ .addConfigBuilder()
+ .addItemDivider(false)
+ .selectMultipleFiles(false)
+ .setRootDirectory(getApp().getProjectsDir().getAbsolutePath())
+ .showHiddenFiles(true)
+ .showOnlyDirectory(true)
+ .theme(R.style.AppTheme_FilePicker)
+ .build()
+ .forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
+ }
+
+ private void openEditor(AndroidProject project, boolean finish) {
+// if (mProgressSheet != null && mProgressSheet.isShowing()) {
+// mProgressSheet.dismiss();
+// }
+ if (project == null) {
+ getApp().toast(R.string.msg_invalid_project, Toaster.Type.ERROR);
+ } else {
+ startActivity(new Intent(this, EditorActivity.class)
+ .putExtra(EditorActivity.EXTRA_PROJECT, project));
+ if (finish)
+ finish();
+ }
+ }
+
+ private void createNewProject() {
+ final String appName = createLayoutBinding.createprojectTextAppName.getEditText().getText().toString().trim();
+ final String packageName = createLayoutBinding.createprojectTextPackageName.getEditText().getText().toString().trim();
+ final int minSdk = getMinSdk();
+ final int targetSdk = getTargetSdk();
+
+ if (!isValid(appName, packageName, minSdk, targetSdk)) {
+ getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
+ return;
+ }
+
+ createProject(appName, packageName, minSdk, targetSdk);
+ }
+
+ private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
+ new TaskExecutor().executeAsync(new ProjectCreatorCallable(mTemplates.get(currentTemplateIndex), new NewProjectDetails(appName, packageName, minSdk, targetSdk), this), r -> {});
+ }
+
+ private boolean isValid(String appName, String packageName, int minSdk, int targetSdk) {
+ return appName != null && appName.length() > 0
+ && packageName != null && packageName.length() > 0
+ && minSdk > 1 && minSdk < 99
+ && targetSdk > 1 && minSdk < 99;
+ }
+
+ private int getMinSdk() {
+ try {
+ return Integer.parseInt(createLayoutBinding.createprojectTextMinSdk.getEditText().getText().toString());
+ } catch (Exception e) { getApp().toast(e.getMessage(), Toaster.Type.ERROR); }
+ return -1;
+ }
+
+ private int getTargetSdk() {
+ try {
+ return Integer.parseInt(createLayoutBinding.createprojectTextTargetSdk.getEditText().getText().toString());
+ } catch (Exception e) { getApp().toast(e.getMessage(), Toaster.Type.ERROR); }
+ return -1;
+ }
+
+ private void showCreateProject() {
+ MaterialContainerTransform transform = TransformUtils.createContainerTransformFor(binding.createProject, binding.createNewCard, binding.realContainer);
+ TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
+ binding.createNewCard.setVisibility(View.VISIBLE);
+ }
+
+ private void hideCreateProject() {
+ MaterialContainerTransform transform = TransformUtils.createContainerTransformFor(binding.createNewCard, binding.createProject, binding.realContainer);
+ TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
+ binding.createNewCard.setVisibility(View.GONE);
+ }
+
+ private final class ProjectReader implements Callable {
+
+ private final File root ;
+
+ public ProjectReader(File root) {
+ this.root = root;
+ }
+
+ @Override
+ public AndroidProject call() throws Exception {
+ return ProjectFinder.fromFolder(root);
+ }
+ }
+
+ private final class OnEndTransitionListener implements Transition.TransitionListener {
+
+ Callable c;
+ public OnEndTransitionListener(Callable c) {
+ this.c = c;
+ }
+
+ @Override
+ public void onTransitionStart(Transition p1) {
+ }
+
+ @Override
+ public void onTransitionEnd(Transition p1) {
+ try {
+ c.call();
+ } catch (Exception e) {}
+ }
+
+ @Override
+ public void onTransitionCancel(Transition p1) {
+ }
+
+ @Override
+ public void onTransitionPause(Transition p1) {
+ }
+
+ @Override
+ public void onTransitionResume(Transition p1) {
+ }
}
}
diff --git a/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java b/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java
index fabe2f733..6c29f29f9 100644
--- a/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java
+++ b/app/src/main/java/com/itsaky/androidide/adapters/viewholders/FileTreeViewHolder.java
@@ -45,7 +45,7 @@ public class FileTreeViewHolder extends TreeNode.BaseNodeViewHolder {
else
icon = R.drawable.ic_file_unknown;
- final boolean isGradle = file.getAbsolutePath().equals(Environment.GRADLE_PROPS_DIR.getAbsolutePath());
+ final boolean isGradle = file.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath());
chevron = binding.getRoot().findViewById(R.id.filetree_chevron);
binding.filetreeName.setText(isGradle ? "GRADLE_HOME" : file.getName());
binding.filetreeIcon.setImageResource(icon);
diff --git a/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java b/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java
index 459c08433..468200af7 100644
--- a/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java
+++ b/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java
@@ -14,13 +14,16 @@ import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import com.itsaky.androidide.R;
import com.itsaky.androidide.services.IDEService;
+import com.itsaky.androidide.utils.Logger;
public abstract class StudioActivity extends AppCompatActivity {
private boolean toRequestStorage = true;
public static final String TAG = "StudioActivity";
public static final int REQCODE_STORAGE = 1009;
-
+
+ protected static Logger LOG = Logger.instance("StudioActivity");
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
diff --git a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
index f0b7cdc67..d665270a5 100644
--- a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
+++ b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java
@@ -11,7 +11,6 @@ import android.text.TextUtils;
import androidx.core.app.NotificationManagerCompat;
import androidx.multidex.MultiDexApplication;
import com.blankj.utilcode.util.EncodeUtils;
-import com.blankj.utilcode.util.FileUtils;
import com.blankj.utilcode.util.ResourceUtils;
import com.blankj.utilcode.util.SPStaticUtils;
import com.blankj.utilcode.util.ThrowableUtils;
@@ -22,14 +21,15 @@ import com.itsaky.androidide.interfaces.LanguageServerListener;
import com.itsaky.androidide.language.java.manager.JavaCharacter;
import com.itsaky.androidide.language.java.server.JavaLanguageServer;
import com.itsaky.androidide.language.xml.completion.XMLCompletionService;
+import com.itsaky.androidide.managers.PreferenceManager;
+import com.itsaky.androidide.managers.ToolsManager;
import com.itsaky.androidide.models.AndroidProject;
-import com.itsaky.androidide.models.ConstantsBridge;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.services.MessagingService;
import com.itsaky.androidide.shell.ShellServer;
import com.itsaky.androidide.utils.Environment;
import com.itsaky.androidide.utils.FileUtil;
-import com.itsaky.androidide.utils.PreferenceManager;
+import com.itsaky.androidide.utils.Logger;
import com.itsaky.androidide.utils.StudioUtils;
import com.itsaky.lsp.LanguageClient;
import com.itsaky.toaster.Toaster;
@@ -39,8 +39,6 @@ import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
-import net.lingala.zip4j.ZipFile;
-import com.blankj.utilcode.util.ZipUtils;
public class StudioApp extends MultiDexApplication
{
@@ -73,6 +71,8 @@ public class StudioApp extends MultiDexApplication
public static final String EMAIL = "contact@androidide.com";
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler;
+
+ private static final Logger LOG = Logger.instance("StudioApp");
@Override
public void onCreate() {
@@ -85,34 +85,15 @@ public class StudioApp extends MultiDexApplication
mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
JavaCharacter.initMap();
- setupLibsIfNeeded();
- extractJlsIfNeeded();
- extractLogsenderIfNeeded();
- extractCleanerIfNeeded();
+ ToolsManager.init(this, null);
-// newShell(line -> handleLog(line)).bgAppend("androidide-cleaner && logcat -v threadtime");
-
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
createNotificationChannels();
}
-
+
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_UPDATE);
FirebaseMessaging.getInstance().subscribeToTopic(MessagingService.TOPIC_DEV_MSGS);
}
-
- private void extractCleanerIfNeeded() {
- File cleanerZip = new File(Environment.TMP_DIR, "bin.zip");
- File cleaner = new File(Environment.BINDIR, "androidide-cleaner");
- if(!cleaner.exists()) {
- ResourceUtils.copyFileFromAssets("data/bin.zip", cleanerZip.getAbsolutePath());
- try {
- ZipUtils.unzipFile(cleanerZip, Environment.BINDIR);
- } catch (Throwable e) {}
- }
-
- if(cleaner.exists() && !cleaner.canExecute())
- cleaner.setExecutable(true);
- }
private void handleLog(CharSequence seq) {
if(seq == null)
@@ -165,67 +146,6 @@ public class StudioApp extends MultiDexApplication
}
- private void extractJlsIfNeeded() {
- boolean gson = false, jls = false, proto = false;
- final List files = FileUtils.listFilesInDir(Environment.JLS_HOME, true);
- for(File f : files) {
- gson = gson == true ? gson : f.getName().equals("gson.jar");
- jls = jls == true ? jls : f.getName().equals("jls.jar");
- proto = proto == true ? proto : f.getName().equals("protobuf.jar");
- }
-
- if(!(gson && jls && proto)) {
- try {
- extractJls();
- } catch (Throwable e) {}
- }
- }
-
- private void extractJls(){
- final File jlsZip = new File(Environment.JLS_HOME, "jls.zip");
- ResourceUtils.copyFileFromAssets("data/jls.zip", jlsZip.getAbsolutePath());
- try {
- ZipFile file = new ZipFile(jlsZip, ConstantsBridge.JLS_ZIP_PASSWORD_HASH.toCharArray());
- file.extractAll(Environment.JLS_HOME.getAbsolutePath());
- FileUtils.delete(jlsZip);
- } catch (Throwable th) {}
- }
-
- private boolean setupLibsIfNeeded() {
- File lib = Environment.LIBDIR;
- if(lib != null && lib.exists() && lib.isDirectory()) {
- boolean libhook, librt, libgcc, libgcc_real, libpthread, libz, libcpp_shared;
- libhook = librt = libgcc = libgcc_real = libpthread = libz = libcpp_shared = false;
- File[] list = lib.listFiles();
- if(list != null) {
- for(File f : list) {
- libhook = libhook == false ? f.getName().equals("libhook.so") : libhook;
- librt = librt == false ? f.getName().equals("librt.so") : librt;
- libpthread = libpthread == false ? f.getName().equals("libpthread.so") : libpthread;
- libgcc = libgcc == false ? f.getName().equals("libgcc.so") : libgcc;
- libgcc_real = libgcc_real = false ? f.getName().equals("libgcc_real.so") : libgcc_real;
- libz = libz == false ? f.getName().equals("libz.so.1") : libz;
- libcpp_shared = libcpp_shared == false ? f.getName().equals("libc++_shared.so") : libcpp_shared;
- }
- } else return extractLibs(lib);
- return !(libhook && librt && libgcc && libgcc_real && libpthread && libz && libcpp_shared) ? extractLibs(lib) : true;
- } else return extractLibs(lib);
- }
-
- private boolean extractLibs(File lib) {
- if(lib.exists())
- FileUtils.delete(lib.getParentFile());
- return lib.mkdirs() && ResourceUtils.copyFileFromAssets(getAssetsDataFile("lib"), lib.getAbsolutePath());
- }
-
- private void extractLogsenderIfNeeded() {
- try {
- final File logsenderZip = new File(Environment.JLS_HOME, "logsender.zip");
- ResourceUtils.copyFileFromAssets("data/logsender.zip", logsenderZip.getAbsolutePath());
- ZipUtils.unzipFile(logsenderZip, Environment.HOME);
- } catch (IOException e) {}
- }
-
private File getBusybox() {
File parentFile = getRootDir().getParentFile();
File busyboxDir = new File(parentFile, "bin/busybox");
@@ -249,38 +169,28 @@ public class StudioApp extends MultiDexApplication
return busyboxDir;
}
+
+ public void stopAllDaemons() {
+ newShell(null).bgAppend("gradle --stop");
+ }
- public void onGradleUpdated() {
- IDEService service = getBuildService();
- if(service != null) {
- service.onGradleUpdated();
- }
+ public void startBuildService(File rootProject) {
+ if(getBuildService() != null) {
+ getBuildService().exit();
+ }
+
+ buildService = new IDEService(rootProject);
+ buildService.start();
}
-
- public void startBuildService() {
- if(!IDEService.isRunning()) {
- startService(new Intent(this, IDEService.class));
- }
- }
-
- public void storeBuildServiceInstance(IDEService.BuildListener listener) {
- buildService = IDEService.getInstance();
- buildService.setListener(listener);
- }
-
+
public IDEService getBuildService() {
return buildService;
}
public void createCompletionService(AndroidProject project, LanguageClient client) {
this.mXmlCompletionService = new XMLCompletionService();
-
- if(languageServer == null) {
- this.languageServer = new JavaLanguageServer(project, client);
- this.languageServer.startServer();
- } else {
- this.languageServer.initialize(project);
- }
+ this.languageServer = new JavaLanguageServer(project, client);
+ this.languageServer.startServer();
}
public JavaLanguageServer getJavaLanguageServer() {
@@ -500,11 +410,15 @@ public class StudioApp extends MultiDexApplication
open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(open);
} catch (Throwable th) {
- Intent open = new Intent();
- open.setAction(Intent.ACTION_VIEW);
- open.setData(Uri.parse(StudioApp.TELEGRAM_GROUP_URL));
- open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- startActivity(open);
+ try {
+ Intent open = new Intent();
+ open.setAction(Intent.ACTION_VIEW);
+ open.setData(Uri.parse(StudioApp.TELEGRAM_GROUP_URL));
+ open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(open);
+ } catch (Throwable th2) {
+ toast(th2.getMessage(), Toaster.Type.ERROR);
+ }
}
}
@@ -515,7 +429,9 @@ public class StudioApp extends MultiDexApplication
open.setData(Uri.parse(StudioApp.SUGGESTIONS_URL));
open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(open);
- } catch (Throwable th) {}
+ } catch (Throwable th) {
+ toast(th.getMessage(), Toaster.Type.ERROR);
+ }
}
public void openWebsite() {
@@ -525,7 +441,9 @@ public class StudioApp extends MultiDexApplication
open.setData(Uri.parse(StudioApp.WEBSITE));
open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(open);
- } catch (Throwable th) {}
+ } catch (Throwable th) {
+ toast(th.getMessage(), Toaster.Type.ERROR);
+ }
}
public void emailUs() {
@@ -535,6 +453,8 @@ public class StudioApp extends MultiDexApplication
open.setData(Uri.parse("mailto:" + EMAIL));
open.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(open);
- } catch (Throwable th) {}
+ } catch (Throwable th) {
+ toast(th.getMessage(), Toaster.Type.ERROR);
+ }
}
}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
index 7013ae075..fe264ee2d 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/EditorFragment.java
@@ -15,12 +15,12 @@ import com.itsaky.androidide.language.java.JavaLanguage;
import com.itsaky.androidide.language.java.JavaLanguageAnalyzer;
import com.itsaky.androidide.language.xml.XMLLanguage;
import com.itsaky.androidide.language.xml.lexer.XMLLexer;
+import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.ConstantsBridge;
import com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE;
import com.itsaky.androidide.tasks.TaskExecutor;
import com.itsaky.androidide.tasks.callables.ReadFileTask;
-import com.itsaky.androidide.utils.PreferenceManager;
import com.itsaky.androidide.utils.TypefaceUtils;
import com.itsaky.lsp.Diagnostic;
import com.itsaky.lsp.DidSaveTextDocumentParams;
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
index d9a912826..1d7c9fd99 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/FileTreeFragment.java
@@ -83,7 +83,7 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
} else if(f.isDirectory() && f.exists()) {
if(node.isExpanded()) {
collapseNode(node);
- } else if(f.getAbsolutePath().equals(Environment.GRADLE_PROPS_DIR.getAbsolutePath()) && !node.isExpanded()) {
+ } else if(f.getAbsolutePath().equals(Environment.GRADLE_USER_HOME.getAbsolutePath()) && !node.isExpanded()) {
expandNode(node);
} else {
setLoading(node);
@@ -164,7 +164,7 @@ public class FileTreeFragment extends BottomSheetDialogFragment implements TreeN
return;
final File gradleProps = Environment.GRADLE_PROPS;
- final File gradleHome = Environment.GRADLE_PROPS_DIR;
+ final File gradleHome = Environment.GRADLE_USER_HOME;
final File root = StudioApp.getInstance().getRootDir().getParentFile();
File projectDir = new File(mProject.getProjectPath());
// projectDir = root;
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/MainFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/MainFragment.java
deleted file mode 100644
index b22b78ad7..000000000
--- a/app/src/main/java/com/itsaky/androidide/fragments/MainFragment.java
+++ /dev/null
@@ -1,340 +0,0 @@
-package com.itsaky.androidide.fragments;
-
-import android.animation.Animator;
-import android.animation.AnimatorListenerAdapter;
-import android.os.Bundle;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.ViewAnimationUtils;
-import android.view.ViewGroup;
-import androidx.constraintlayout.widget.ConstraintLayout;
-import androidx.transition.ChangeBounds;
-import androidx.transition.Transition;
-import androidx.transition.TransitionManager;
-import com.itsaky.androidide.R;
-import com.itsaky.androidide.databinding.LayoutCreateProjectBinding;
-import com.itsaky.androidide.databinding.LayoutCreateProjectContentBinding;
-import com.itsaky.androidide.databinding.LayoutMainBinding;
-import com.itsaky.androidide.interfaces.ProjectWriterCallback;
-import com.itsaky.androidide.models.ProjectTemplate;
-import com.itsaky.androidide.views.transition.ArcMotionPlus;
-import com.itsaky.toaster.Toaster;
-import java.util.ArrayList;
-import java.util.concurrent.Callable;
-import com.itsaky.androidide.utils.ProjectWriter;
-import com.itsaky.androidide.models.NewProjectDetails;
-import com.itsaky.androidide.fragments.sheets.ProgressSheet;
-import com.itsaky.androidide.tasks.TaskExecutor;
-import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable;
-
-public class MainFragment extends BaseFragment implements OnClickListener, ProjectWriterCallback
-{
- private LayoutMainBinding binding;
- private LayoutCreateProjectBinding createBinding;
- private LayoutCreateProjectContentBinding createLayoutBinding;
-
- private OnCreateProjectListener createProjectListener;
- private ProgressSheet mWritingProgressSheet;
-
- private ArrayList mTemplates = new ArrayList();
- private OnCreateProjectVisibilityListener visibilityChangeListener;
-
- public static final String TAG = "MainFragment";
- private int currentTemplateIndex = 0;
-
- public MainFragment setCreateProjectListener(OnCreateProjectListener createProjectListener) {
- this.createProjectListener = createProjectListener;
- return this;
- }
-
- public MainFragment setOnCreateProjectVisibilityListener(OnCreateProjectVisibilityListener visibilityChangeListener) {
- this.visibilityChangeListener = visibilityChangeListener;
- return this;
- }
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
- binding = LayoutMainBinding.inflate(inflater, container, false);
- createBinding = binding.createLayout;
- createLayoutBinding = createBinding.createProjectContent;
- return binding.getRoot();
- }
-
- @Override
- public void onViewCreated(final View view, Bundle savedInstanceState) {
- super.onViewCreated(view, savedInstanceState);
-
- binding.createNew.setOnClickListener(this);
- createBinding.createprojectClose.setOnClickListener(this);
- createLayoutBinding.createprojectCreate.setOnClickListener(this);
- createLayoutBinding.previousCard.setOnClickListener(this);
- createLayoutBinding.nextCard.setOnClickListener(this);
- binding.createNewCard.setVisibility(View.GONE);
-
- currentTemplateIndex = 0;
- createTemplates();
- createProgressSheet();
- showTemplate(currentTemplateIndex);
- }
-
- @Override
- public void onClick(View p1) {
- if(p1.getId() == binding.createNew.getId())
- showCreateProject();
- else if(p1.getId() == createBinding.createprojectClose.getId())
- hideCreateProject();
- else if(p1.getId() == createLayoutBinding.nextCard.getId()) {
- currentTemplateIndex++;
- if(currentTemplateIndex >= mTemplates.size()) currentTemplateIndex = 0;
- showTemplate(currentTemplateIndex);
- } else if(p1.getId() == createLayoutBinding.previousCard.getId()) {
- currentTemplateIndex--;
- if(currentTemplateIndex < 0) currentTemplateIndex = mTemplates.size() - 1;
- showTemplate(currentTemplateIndex);
- } else if(p1.getId() == createLayoutBinding.createprojectCreate.getId())
- createNewProject();
- }
-
- @Override
- public void beforeBegin() {
- if(mWritingProgressSheet == null)
- createProgressSheet();
-
- setMessage(R.string.msg_begin_project_write);
- }
-
- @Override
- public void onProcessTask(String taskName) {
- setMessage(taskName);
- }
-
- @Override
- public void onSuccess() {
- if(mWritingProgressSheet != null)
- createProgressSheet();
-
- if(mWritingProgressSheet.isShowing())
- mWritingProgressSheet.dismiss();
- getStudioActivity().getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
- if(createProjectListener != null)
- createProjectListener.onNewProjectCreated();
- }
-
- @Override
- public void onFailed(String reason) {
- if(mWritingProgressSheet != null)
- createProgressSheet();
-
- if(mWritingProgressSheet.isShowing())
- mWritingProgressSheet.dismiss();
- getStudioActivity().getApp().toast(reason, Toaster.Type.ERROR);
- }
-
- private void showTemplate(int index) {
- if(index < 0 || index >= mTemplates.size()) return;
- final ProjectTemplate template = mTemplates.get(index);
- createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
- createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
- createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
- }
-
- /**
- * Must be called in/after onViewCreated
- */
- private void createTemplates() {
- mTemplates = new ArrayList<>();
-
- ProjectTemplate
- empty = new ProjectTemplate()
- .setId(0)
- .setName(getContext(), R.string.template_empty)
- .setDescription(getContext(), R.string.template_description_empty)
- .setImageId(R.drawable.template_empty),
- basic = new ProjectTemplate()
- .setId(1)
- .setName(getContext(), R.string.template_basic)
- .setDescription(getContext(), R.string.template_description_basic)
- .setImageId(R.drawable.template_basic),
- drawer = new ProjectTemplate()
- .setId(2)
- .setName(getContext(), R.string.template_navigation_drawer)
- .setDescription(getContext(), R.string.template_description_navigation_drawer)
- .setImageId(R.drawable.template_navigation_drawer);
-// navigation = new ProjectTemplate()
-// .setId(3)
-// .setName(getContext(), R.string.template_bottom_navigation)
-// .setDescription(getContext(), R.string.template_description_bottom_navigation)
-// .setImageId(R.drawable.template_bottom_navigation),
-// kotlin = new ProjectTemplate()
-// .setId(4)
-// .setName(getContext(), R.string.template_basic_kotlin)
-// .setDescription(getContext(), R.string.template_description_kotlin)
-// .setImageId(R.drawable.template_kotlin);
-
- mTemplates.add(empty);
- mTemplates.add(basic);
- mTemplates.add(drawer);
-// mTemplates.add(navigation);
-// mTemplates.add(kotlin);
- }
-
- private void createProgressSheet() {
- mWritingProgressSheet = new ProgressSheet();
- }
-
- private void setMessage(int msg) {
- if(getContext() != null) {
- setMessage(getContext().getString(msg));
- }
- }
-
- private void setMessage(String msg) {
- mWritingProgressSheet.setMessage(msg);
- }
-
- private void createNewProject() {
- final String appName = createLayoutBinding.createprojectTextAppName.getEditText().getText().toString().trim();
- final String packageName = createLayoutBinding.createprojectTextPackageName.getEditText().getText().toString().trim();
- final int minSdk = getMinSdk();
- final int targetSdk = getTargetSdk();
-
- if(!isValid(appName, packageName, minSdk, targetSdk)) {
- getStudioActivity().getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
- return;
- }
-
- createProject(appName, packageName, minSdk, targetSdk);
- }
-
- private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
- new TaskExecutor().executeAsync(new ProjectCreatorCallable(mTemplates.get(currentTemplateIndex), new NewProjectDetails(appName, packageName, minSdk, targetSdk), this), r -> {});
- }
-
- private boolean isValid(String appName, String packageName, int minSdk, int targetSdk)
- {
- return appName != null && appName.length() > 0
- && packageName != null && packageName.length() > 0
- && minSdk > 1 && minSdk < 99
- && targetSdk > 1 && minSdk < 99;
- }
-
- private int getMinSdk()
- {
- try
- {
- return Integer.parseInt(createLayoutBinding.createprojectTextMinSdk.getEditText().getText().toString());
- } catch (Exception e)
- { getStudioActivity().getApp().toast(e.getMessage(), Toaster.Type.ERROR); }
- return -1;
- }
-
- private int getTargetSdk()
- {
- try
- {
- return Integer.parseInt(createLayoutBinding.createprojectTextTargetSdk.getEditText().getText().toString());
- } catch (Exception e)
- { getStudioActivity().getApp().toast(e.getMessage(), Toaster.Type.ERROR); }
- return -1;
- }
-
- private void showCreateProject()
- {
- ChangeBounds bounds = new ChangeBounds();
- bounds.setPathMotion(new ArcMotionPlus().setArcAngle(90));
- bounds.setDuration(300);
- bounds.addListener(new OnEndTransitionListener(() -> {
- final int x = (int) binding.createNew.getX() + (binding.createNew.getWidth() / 2);
- final int y = (int) binding.createNew.getY() + (binding.createNew.getHeight() / 2);
- binding.createNewCard.setVisibility(View.VISIBLE);
- int startRadius = binding.createNew.getWidth() / 2;
- int endRadius = Math.max(binding.getRoot().getWidth(), binding.getRoot().getHeight());
- Animator anim = ViewAnimationUtils.createCircularReveal(binding.createNewCard, x, y, startRadius, endRadius);
- anim.setDuration(600);
- anim.start();
- if(visibilityChangeListener != null)
- visibilityChangeListener.onVisibilityChanged(true);
- return null;
- }));
- TransitionManager.beginDelayedTransition(binding.getRoot(), bounds);
-
- ConstraintLayout.LayoutParams p = (ConstraintLayout.LayoutParams) binding.createNew.getLayoutParams();
- p.topToTop = p.PARENT_ID;
- p.rightToRight = p.PARENT_ID;
- p.leftToLeft = p.UNSET;
- binding.createNew.setLayoutParams(p);
- }
-
- private void hideCreateProject()
- {
- int x = (int) binding.createNew.getX() + (binding.createNew.getWidth() / 2);
- int y = (int) binding.createNew.getY() + (binding.createNew.getHeight() / 2);
- int startRadius = Math.max(binding.getRoot().getWidth(), binding.getRoot().getHeight());
- int endRadius = binding.createNew.getWidth() / 2;
- Animator anim = ViewAnimationUtils.createCircularReveal(binding.createNewCard, x, y, startRadius, endRadius);
- anim.setDuration(500);
- anim.addListener(new AnimatorListenerAdapter()
- {
- @Override
- public void onAnimationEnd(Animator anim)
- {
- binding.createNewCard.setVisibility(View.GONE);
- if(visibilityChangeListener != null)
- visibilityChangeListener.onVisibilityChanged(false);
-
- ChangeBounds bounds = new ChangeBounds();
- bounds.setPathMotion(new ArcMotionPlus().setArcAngle(90));
- bounds.setDuration(300);
- TransitionManager.beginDelayedTransition(binding.getRoot(), bounds);
-
- ConstraintLayout.LayoutParams p = (ConstraintLayout.LayoutParams) binding.createNew.getLayoutParams();
- p.topToTop = p.UNSET;
- p.topToBottom = binding.greetingText.getId();
- p.rightToRight = p.PARENT_ID;
- p.leftToLeft = p.PARENT_ID;
- binding.createNew.setLayoutParams(p);
- }
- });
- anim.start();
- }
-
- private final class OnEndTransitionListener implements Transition.TransitionListener {
-
- Callable c;
- public OnEndTransitionListener(Callable c) {
- this.c = c;
- }
-
- @Override
- public void onTransitionStart(Transition p1) {
- }
-
- @Override
- public void onTransitionEnd(Transition p1) {
- try {
- c.call();
- } catch (Exception e) {}
- }
-
- @Override
- public void onTransitionCancel(Transition p1) {
- }
-
- @Override
- public void onTransitionPause(Transition p1) {
- }
-
- @Override
- public void onTransitionResume(Transition p1) {
- }
- }
-
- public interface OnCreateProjectVisibilityListener {
- public void onVisibilityChanged(boolean isVisible);
- }
-
- public static interface OnCreateProjectListener {
- public void onNewProjectCreated();
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java
index 759bdbcac..f4c956351 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BasePreferenceFragment.java
@@ -1,13 +1,13 @@
package com.itsaky.androidide.fragments.preferences;
-import androidx.preference.PreferenceFragmentCompat;
+import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
-import android.os.Bundle;
import android.view.ViewGroup;
import androidx.core.content.ContextCompat;
+import androidx.preference.PreferenceFragmentCompat;
import com.itsaky.androidide.app.StudioApp;
-import com.itsaky.androidide.utils.PreferenceManager;
+import com.itsaky.androidide.managers.PreferenceManager;
public abstract class BasePreferenceFragment extends PreferenceFragmentCompat {
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java
index 9dd99de65..eca3dcba5 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java
@@ -20,7 +20,7 @@ import java.io.File;
import java.util.List;
import java.util.Set;
-import static com.itsaky.androidide.utils.PreferenceManager.*;
+import static com.itsaky.androidide.managers.PreferenceManager.*;
public class BuildPreferences extends BasePreferenceFragment implements Preference.OnPreferenceClickListener {
diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java
index 47515d9a7..c40da0695 100644
--- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java
+++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/EditorPreferences.java
@@ -10,7 +10,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.itsaky.androidide.R;
import com.itsaky.androidide.databinding.LayoutTextSizeSliderBinding;
-import static com.itsaky.androidide.utils.PreferenceManager.*;
+import static com.itsaky.androidide.managers.PreferenceManager.*;
import com.itsaky.androidide.models.ConstantsBridge;
import com.google.android.material.slider.LabelFormatter;
import java.text.NumberFormat;
diff --git a/app/src/main/java/com/itsaky/androidide/interfaces/ProjectWriterCallback.java b/app/src/main/java/com/itsaky/androidide/interfaces/ProjectWriterCallback.java
index 1642923b5..51ed7d62d 100644
--- a/app/src/main/java/com/itsaky/androidide/interfaces/ProjectWriterCallback.java
+++ b/app/src/main/java/com/itsaky/androidide/interfaces/ProjectWriterCallback.java
@@ -1,8 +1,10 @@
package com.itsaky.androidide.interfaces;
+import java.io.File;
+
public interface ProjectWriterCallback {
public void beforeBegin();
public void onProcessTask(String taskName);
- public void onSuccess();
+ public void onSuccess(File rootDir);
public void onFailed(String reason);
}
diff --git a/app/src/main/java/com/itsaky/androidide/language/buildout/BuildOutputLanguage.java b/app/src/main/java/com/itsaky/androidide/language/buildout/BuildOutputLanguage.java
new file mode 100644
index 000000000..23ead11ac
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/language/buildout/BuildOutputLanguage.java
@@ -0,0 +1,61 @@
+package com.itsaky.androidide.language.buildout;
+
+import io.github.rosemoe.editor.interfaces.CodeAnalyzer;
+import io.github.rosemoe.editor.langs.EmptyLanguage;
+import io.github.rosemoe.editor.text.TextAnalyzeResult;
+import io.github.rosemoe.editor.text.TextAnalyzer;
+import io.github.rosemoe.editor.widget.EditorColorScheme;
+import java.util.ArrayList;
+import java.util.List;
+
+public class BuildOutputLanguage extends EmptyLanguage {
+
+ private final BuildAnalyzer analyzer = new BuildAnalyzer();
+
+ @Override
+ public CodeAnalyzer getAnalyzer() {
+ return analyzer;
+ }
+
+ public void addError(int line) {
+ analyzer.addError(line);
+ }
+
+ public void addSuccess(int line) {
+ analyzer.addSuccess(line);
+ }
+
+ private class BuildAnalyzer implements CodeAnalyzer {
+
+ private final List errorMap = new ArrayList<>();
+ private final List successMap = new ArrayList<>();
+
+ public void addError(int line) {
+ errorMap.add(line);
+ }
+
+ public void addSuccess(int line) {
+ successMap.add(line);
+ }
+
+ @Override
+ public void analyze(CharSequence content, TextAnalyzeResult colors, TextAnalyzer.AnalyzeThread.Delegate delegate) {
+ int line = 0;
+ int index = 0;
+ colors.addNormalIfNull();
+ while(index < content.length() && delegate.shouldAnalyze()) {
+ int id = EditorColorScheme.TEXT_NORMAL;
+ if(errorMap != null && errorMap.contains(line))
+ id = EditorColorScheme.BUILD_OUT_ERROR;
+ else if(successMap != null && successMap.contains(line))
+ id = EditorColorScheme.BUILD_OUT_SUCCESS;
+ colors.addIfNeeded(line, 0, id);
+ char c = content.charAt(index);
+ if(c == '\n') {
+ line++;
+ }
+ index++;
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/language/java/server/JavaLanguageServer.java b/app/src/main/java/com/itsaky/androidide/language/java/server/JavaLanguageServer.java
index 4f478997b..7d42a4a7a 100644
--- a/app/src/main/java/com/itsaky/androidide/language/java/server/JavaLanguageServer.java
+++ b/app/src/main/java/com/itsaky/androidide/language/java/server/JavaLanguageServer.java
@@ -81,7 +81,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
"--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED " +
"--add-opens jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED " +
"-cp $JLS_HOME/lib/jls.jar:$JLS_HOME/lib/gson.jar:$JLS_HOME/lib/protobuf.jar " +
- "org.javacs.Main --quiet";
+ "org.javacs.Main";
private static final String CONTENT_LENGTH = "Content-Length: ";
@@ -113,6 +113,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
onServerOut(temp);
});
}
+ socket.close();
logger.i("Socket disconnected");
} catch (Throwable e) {
logEx(e);
@@ -167,6 +168,13 @@ public class JavaLanguageServer implements ShellServer.Callback {
});
}
+ public void exit() {
+ write(Method.EXIT, "");
+ try {
+ server.close();
+ } catch (Throwable th) {}
+ }
+
public boolean isStarted() {
return started;
}
@@ -287,8 +295,7 @@ public class JavaLanguageServer implements ShellServer.Callback {
}
private BufferedOutputStream os;
- private BufferedOutputStream os1;
-
+
private void writeOut(String data) {
try {
if(os == null) {
@@ -300,24 +307,11 @@ public class JavaLanguageServer implements ShellServer.Callback {
logger.e(ThrowableUtils.getFullStackTrace(th));
}
}
-
- private void writeOut2(String data) {
- try {
- if(os1 == null) {
- os1 = new BufferedOutputStream(new FileOutputStream(new File(FileUtil.getExternalStorageDir(), "ide_xlog/verbose.txt"), true));
- }
- os1.write(data.getBytes());
- os1.flush();
- } catch (Throwable th) {
- logger.e(ThrowableUtils.getFullStackTrace(th));
- }
- }
/**
* This method is called on UI Thread, do not perform time consuming or networking tasks
*/
private void onServerOut(String line) {
- writeOut2(line + "\n");
if(line.contains(CONTENT_LENGTH)) line = line.substring(0, line.lastIndexOf(CONTENT_LENGTH));
if(this.client == null) return;
try {
diff --git a/app/src/main/java/com/itsaky/androidide/managers/PreferenceManager.java b/app/src/main/java/com/itsaky/androidide/managers/PreferenceManager.java
new file mode 100644
index 000000000..a65350b5b
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/managers/PreferenceManager.java
@@ -0,0 +1,187 @@
+package com.itsaky.androidide.managers;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.content.SharedPreferences;
+import com.itsaky.androidide.utils.Environment;
+import java.util.HashSet;
+import java.util.Set;
+
+public class PreferenceManager {
+
+ private SharedPreferences prefs;
+ private SharedPreferences.Editor editor;
+
+ public static final int LOGSENDER_LATEST_VERSION = 102;
+
+ public static final String KEY_FRAMEWORK_DOWNLOADED = "framework_downloaded";
+ public static final String KEY_FRAMEWORK_INSTALLED = "framework_installed";
+ public static final String KEY_IS_FIRST_PROJECT_BUILD = "project_isFirstBuild";
+
+ public static final String KEY_EDITORFLAG_WS_LEADING = "idepref_editor_wsLeading";
+ public static final String KEY_EDITORFLAG_WS_TRAILING = "idepref_editor_wsTrailing";
+ public static final String KEY_EDITORFLAG_WS_INNER = "idepref_editor_wsInner";
+ public static final String KEY_EDITORFLAG_WS_EMPTY_LINE = "idepref_editor_wsEmptyLine";
+ public static final String KEY_EDITORFLAG_LINE_BREAK = "idepref_editor_lineBreak";
+ public static final String KEY_EDITOR_DRAW_HEX = "idepref_editor_drawHexColors";
+
+ public static final String KEY_GRADLE_CURRENT_DIR = "idepref_customGradle_currentFolder";
+ public static final String KEY_GRADLE_FILENAMES = "idepref_customGradle_folderNames";
+ public static final String KEY_GRADLECMD_STACKTRACRE = "idepref_gradleCmd_stacktrace";
+ public static final String KEY_GRADLECMD_DEBUG = "idepref_gradleCmd_debug";
+ public static final String KEY_GRADLECMD_SCAN = "idepref_gradleCmd_scan";
+ public static final String KEY_GRADLECMD_INFO = "idepref_gradleCmd_info";
+ public static final String KEY_GRADLECMD_WARNINGMODE = "idepref_gradleCmd_warningMode";
+
+ public static final String KEY_LOGSENDER_CURRENT_VERSION = "logsender_currentVersion";
+
+ public static final String KEY_LAST_OPENED_PROJECT = "ide_last_project";
+ public static final String NO_OPENED_PROJECT = "";
+
+ @SuppressLint("CommitPrefEdits")
+ public PreferenceManager(Context ctx) {
+ this.prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(ctx);
+ this.editor = prefs.edit();
+ }
+
+ public PreferenceManager putInt(String key, int val) {
+ editor.putInt(key, val).apply();
+ return this;
+ }
+
+ public int getInt(String key) {
+ return prefs.getInt(key, 0);
+ }
+
+ public int getInt(String key, int def) {
+ return prefs.getInt(key, def);
+ }
+
+ public PreferenceManager putFloat(String key, float val) {
+ editor.putFloat(key, val).apply();
+ return this;
+ }
+
+ public float getFloat(String key) {
+ return prefs.getFloat(key, 0f);
+ }
+
+ public float getFloat(String key, float def) {
+ return prefs.getFloat(key, def);
+ }
+
+ public PreferenceManager putBoolean(String key, boolean value) {
+ editor.putBoolean(key, value).apply();
+ return this;
+ }
+
+ public boolean getBoolean(String key) {
+ return getBoolean(key, false);
+ }
+
+ public boolean getBoolean(String key, boolean defaultValue) {
+ return prefs.getBoolean(key, defaultValue);
+ }
+
+ public PreferenceManager putString(String key, String value) {
+ editor.putString(key, value).apply();
+ return this;
+ }
+
+ public String getString(String key) {
+ return prefs.getString(key, null);
+ }
+
+ public String getString(String key, String defaultValue) {
+ return prefs.getString(key, defaultValue);
+ }
+
+ public PreferenceManager setOpenedProject(String root) {
+ return putString(KEY_LAST_OPENED_PROJECT, root);
+ }
+
+ public String getOpenedProject() {
+ return getString(KEY_LAST_OPENED_PROJECT, NO_OPENED_PROJECT);
+ }
+
+ public boolean wasProjectOpened() {
+ return !getOpenedProject().equals(NO_OPENED_PROJECT);
+ }
+
+ public boolean isFrameworkDownloaded() {
+ return getBoolean(KEY_FRAMEWORK_DOWNLOADED);
+ }
+
+ public boolean isFrameworkInstalled() {
+ return isFrameworkDownloaded() && getBoolean(KEY_FRAMEWORK_INSTALLED);
+ }
+
+ public boolean isStracktraceEnabled() {
+ return getBoolean(KEY_GRADLECMD_STACKTRACRE);
+ }
+
+ public boolean isGradleInfoEnabled() {
+ return getBoolean(KEY_GRADLECMD_INFO, true);
+ }
+
+ public boolean isGradleDebugEnabled() {
+ return getBoolean(KEY_GRADLECMD_DEBUG);
+ }
+
+ public boolean isGradleScanEnabled() {
+ return getBoolean(KEY_GRADLECMD_SCAN);
+ }
+
+ public boolean isGradleWarningEnabled() {
+ return getBoolean(KEY_GRADLECMD_WARNINGMODE);
+ }
+
+ public PreferenceManager setGradleStacktraceEnabled(boolean enabled) {
+ return putBoolean(KEY_GRADLECMD_STACKTRACRE, enabled);
+ }
+
+ public PreferenceManager setGradleInfoEnabled(boolean enabled) {
+ return putBoolean(KEY_GRADLECMD_INFO, enabled);
+ }
+
+ public PreferenceManager setGradleDebugEnabled(boolean enabled) {
+ return putBoolean(KEY_GRADLECMD_DEBUG, enabled);
+ }
+
+ public PreferenceManager setGradleScanEnabled(boolean enabled) {
+ return putBoolean(KEY_GRADLECMD_SCAN, enabled);
+ }
+
+ public PreferenceManager setGradleWarningEnabled(boolean enabled) {
+ return putBoolean(KEY_GRADLECMD_WARNINGMODE, enabled);
+ }
+
+ public PreferenceManager addGradleFolder(String name) {
+ Set folders = prefs.getStringSet(KEY_GRADLE_FILENAMES, new HashSet());
+ if(folders == null) {
+ folders = new HashSet();
+ }
+ if(!folders.contains(name)) {
+ folders.add(name);
+ }
+ editor.putStringSet(KEY_GRADLE_FILENAMES, folders).apply();
+ return this;
+ }
+
+ public Set getGradleFolderNames() {
+ Set folders = prefs.getStringSet(KEY_GRADLE_FILENAMES, new HashSet());
+ if(folders != null && folders.size() > 0) {
+ return folders;
+ } else {
+ return addGradleFolder(Environment.DEFAULT_GRADLE_FOLDER).getGradleFolderNames();
+ }
+ }
+
+ public PreferenceManager setCurentGradleFolderName(String name) {
+ return addGradleFolder(name).putString(KEY_GRADLE_CURRENT_DIR, name);
+ }
+
+ public String getCurrentGradleFolderName() {
+ return getString(KEY_GRADLE_CURRENT_DIR, Environment.DEFAULT_GRADLE_FOLDER);
+ }
+}
diff --git a/app/src/main/java/com/itsaky/androidide/managers/ToolsManager.java b/app/src/main/java/com/itsaky/androidide/managers/ToolsManager.java
new file mode 100644
index 000000000..392eb051a
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/managers/ToolsManager.java
@@ -0,0 +1,208 @@
+package com.itsaky.androidide.managers;
+
+import com.blankj.utilcode.util.FileIOUtils;
+import com.blankj.utilcode.util.FileUtils;
+import com.blankj.utilcode.util.ResourceUtils;
+import com.blankj.utilcode.util.ZipUtils;
+import com.itsaky.androidide.app.StudioApp;
+import com.itsaky.androidide.models.ConstantsBridge;
+import com.itsaky.androidide.utils.Environment;
+import com.itsaky.androidide.utils.Logger;
+import java.io.File;
+import java.io.IOException;
+import java.util.List;
+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;
+ 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 void init(StudioApp app, Runnable onFinish) {
+ prefs = app.getPrefManager();
+ extractJlsIfNeeded();
+ extractLogsenderIfNeeded();
+ extractCleanerIfNeeded();
+ extractGradleApiIfNeeded();
+ writeInitScript(app);
+
+ if(onFinish != null)
+ onFinish.run();
+ }
+
+ private static void writeInitScript(StudioApp app) {
+ FileIOUtils.writeFileFromString(Environment.INIT_SCRIPT, INIT_SCRIPT);
+ }
+
+
+ private static void extractJlsIfNeeded() {
+ boolean gson = false, jls = false, proto = false;
+ final List files = FileUtils.listFilesInDir(Environment.JLS_HOME, true);
+ for(File f : files) {
+ gson = gson == true ? gson : f.getName().equals("gson.jar");
+ jls = jls == true ? jls : f.getName().equals("jls.jar");
+ proto = proto == true ? proto : f.getName().equals("protobuf.jar");
+ }
+
+ final boolean exists = gson && jls && proto;
+ final boolean isOld = JLS_VERSION > prefs.getInt(KEY_JLS_VERSION, 0);
+ if(!exists || isOld) {
+ try {
+ extractJls();
+ } catch (Throwable e) {}
+ }
+ }
+
+ private static void extractJls(){
+ final File jlsZip = new File(Environment.JLS_HOME, "jls.zip");
+ ResourceUtils.copyFileFromAssets("data/jls.zip", jlsZip.getAbsolutePath());
+ try {
+ ZipFile file = new ZipFile(jlsZip, ConstantsBridge.JLS_ZIP_PASSWORD_HASH.toCharArray());
+ file.extractAll(Environment.JLS_HOME.getAbsolutePath());
+ FileUtils.delete(jlsZip);
+ prefs.putInt(KEY_JLS_VERSION, JLS_VERSION);
+ } catch (Throwable th) {}
+ }
+
+
+
+ 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("data/logsender.zip", logsenderZip.getAbsolutePath());
+ ZipUtils.unzipFile(logsenderZip, Environment.HOME);
+ prefs.putInt(KEY_LOGSENDER_VERSION, LOGSENDER_VERSION);
+ }
+ } 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 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"
+ + "\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}"
+ + "\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 }"
+ + "\n }"
+ + "\n}"
+ + "\n"
+ + "\nclass AndroidIDEPlugin implements Plugin {"
+ + "\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 }"
+ + "\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\""
+ + "\n}";
+
+ private static final Logger LOG = Logger.instance("ToolsManager");
+}
diff --git a/app/src/main/java/com/itsaky/androidide/utils/VersionedFileManager.java b/app/src/main/java/com/itsaky/androidide/managers/VersionedFileManager.java
similarity index 91%
rename from app/src/main/java/com/itsaky/androidide/utils/VersionedFileManager.java
rename to app/src/main/java/com/itsaky/androidide/managers/VersionedFileManager.java
index 96833e091..c3c6d3d36 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/VersionedFileManager.java
+++ b/app/src/main/java/com/itsaky/androidide/managers/VersionedFileManager.java
@@ -1,21 +1,23 @@
-package com.itsaky.androidide.utils;
+package com.itsaky.androidide.managers;
+
import java.io.File;
import java.util.HashMap;
public class VersionedFileManager {
- private static final HashMap map = new HashMap<>();
+ private static final HashMap map = new HashMap<>();
+
public static int fileOpened(File file) {
// Initial version of file should always be 1
map.put(file, 1);
return 1;
}
-
+
public static int incrementVersion(File file) {
if(!map.containsKey(file)) {
map.put(file, 1);
}
-
+
int version = map.get(file);
version++;
map.put(file, version);
diff --git a/app/src/main/java/com/itsaky/androidide/models/AndroidProject.java b/app/src/main/java/com/itsaky/androidide/models/AndroidProject.java
index c6e262e21..5f7461796 100644
--- a/app/src/main/java/com/itsaky/androidide/models/AndroidProject.java
+++ b/app/src/main/java/com/itsaky/androidide/models/AndroidProject.java
@@ -9,6 +9,7 @@ import java.util.ArrayList;
import java.util.List;
public class AndroidProject implements Parcelable {
+
private String appName;
private String packageName;
private String iconPath;
diff --git a/app/src/main/java/com/itsaky/androidide/models/ConstantsBridge.java b/app/src/main/java/com/itsaky/androidide/models/ConstantsBridge.java
index bc060b279..5ee04038f 100644
--- a/app/src/main/java/com/itsaky/androidide/models/ConstantsBridge.java
+++ b/app/src/main/java/com/itsaky/androidide/models/ConstantsBridge.java
@@ -15,4 +15,8 @@ public class ConstantsBridge {
// Real password is admin@androidide.com
// This will he further encrypted by StringFog at compile time
public static final String JLS_ZIP_PASSWORD_HASH = "865dabea6f06fea1a3dd572e58a4a81c";
+
+ // Password for GradleApi zip
+ // Real password is gradle-api@admin@androidide
+ public static final String GRADLE_API_ZIP_PASSWORD_HASH = "b50b462f8e8c5789f32428d7e1967da2";
}
diff --git a/app/src/main/java/com/itsaky/androidide/services/IDEService.java b/app/src/main/java/com/itsaky/androidide/services/IDEService.java
index 393cffd4c..d8431b950 100644
--- a/app/src/main/java/com/itsaky/androidide/services/IDEService.java
+++ b/app/src/main/java/com/itsaky/androidide/services/IDEService.java
@@ -1,55 +1,76 @@
package com.itsaky.androidide.services;
-import android.app.Service;
-import android.content.Intent;
-import android.os.IBinder;
+import androidx.annotation.StringRes;
+import com.blankj.utilcode.util.CloseUtils;
import com.blankj.utilcode.util.FileUtils;
-import com.itsaky.androidide.R;
+import com.google.gson.GsonBuilder;
import com.itsaky.androidide.app.StudioApp;
-import com.itsaky.androidide.models.gradle.Artifact;
+import com.itsaky.androidide.managers.PreferenceManager;
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.PreferenceManager;
+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;
import java.util.Calendar;
import java.util.Date;
-import java.util.HashMap;
import java.util.List;
import java.util.Locale;
-import java.util.Map;
-import org.json.JSONObject;
+import java.util.stream.Collectors;
+
+public class IDEService extends SocketConnection implements ShellServer.Callback {
-public class IDEService extends Service implements ShellServer.Callback {
-
- private static IDEService instance;
private ShellServer shell;
private BuildListener listener;
- private File module;
+ private File rootProject;
private GradleTask currentTask;
-
- private List dependencies;
-
+ private StudioApp app;
+
+ private IDEProject project;
+ private Module main;
+
+ private boolean isConnected;
+ private boolean projectInitialized = false;
private boolean isBuilding = false;
- private boolean outputtingJars = false;
- private boolean recreateShellOnDone = false;
-
- private static boolean isRunning = 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 <<";
-
- public static final String ACTION_CREATED = "BUILD_SERVICE_CREATED";
-
+ private boolean isRunning = false;
+
+ private long buildStart = 0;
+ private int UNIVERSAL_ID = 0;
+
+ public static final String SUCCESSFUL = "SUCCESSFUL";
+ public static final String FAILED = "FAILED";
+
public static final int TASK_SHOW_DEPENDENCIES = 0;
public static final int TASK_ASSEMBLE_DEBUG = 1;
public static final int TASK_ASSEMBLE_RELEASE = 2;
@@ -62,351 +83,313 @@ public class IDEService extends Service implements ShellServer.Callback {
public static final int TASK_LINT = 9;
public static final int TASK_LINT_DEBUG = 10;
public static final int TASK_LINT_RELEASE = 11;
-
-
- public IDEService() {}
-
- public IDEService(String module) {
- this(new File(module));
- }
-
- public IDEService(File module) {
- this.module = module;
+
+
+ private static final String START_COMMAND = "java " +
+ "-cp %s" +
+ " com.itsaky.gradle.tooling.api.GradleApi";
+
+ static {
+ RuntimeTypeAdapterFactory descriptorFactory = RuntimeTypeAdapterFactory.of(OperationDescriptor.class)
+ .registerSubtype(OperationDescriptor.class)
+ .registerSubtype(TaskDescriptor.class)
+ .registerSubtype(FinishEventDescriptor.class)
+ .registerSubtype(ProjectConfigurationDescriptor.class)
+ .registerSubtype(StartEventDescriptor.class)
+ .registerSubtype(StatusEventDescriptor.class)
+ .registerSubtype(TestOutDescriptor.class)
+ .registerSubtype(TransformDescriptor.class)
+ .registerSubtype(WorkItemDescriptor.class);
+ RuntimeTypeAdapterFactory pluginFactory = RuntimeTypeAdapterFactory.of(PluginIdentifierDescription.class)
+ .registerSubtype(PluginIdentifierDescription.class)
+ .registerSubtype(BinaryPluginIdentifierDescription.class)
+ .registerSubtype(ScriptPluginIdentifierDescription.class);
+ GSON = new GsonBuilder()
+ .registerTypeAdapterFactory(descriptorFactory)
+ .registerTypeAdapterFactory(pluginFactory)
+ .create();
+ }
+
+ public IDEService(File rootProject) {
+ super(GradleConnection.SERVER_HOST, GradleConnection.SERVER_PORT);
+ this.app = StudioApp.getInstance();
+ this.rootProject = rootProject;
+
+ this.shell = app.newShell(this);
+ this.shell.bgAppend(String.format(START_COMMAND, getClasspaths()));
+
+ isRunning = true;
}
- public void onGradleUpdated() {
- if(shell == null) {
- createShell();
- return;
- }
- if(!isBuilding()) {
- createShell();
- } else {
- recreateShellOnDone = true;
- }
- }
-
- public static IDEService getInstance() {
- return instance;
- }
+ private String getClasspaths() {
+ final List 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);
+ }
+ }
+
+ return sb.toString();
+ }
+
+ @Override
+ protected void onConnected() {
+ isConnected = true;
+ writeMessage(Method.PROJECT_INIT, new InitializeParams(Environment.GRADLE_DIR.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 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
+ }
+ 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;
+ }
+ }
+
+ @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;
}
-
- public IDEService setModule(String path) {
- return setModule(new File(path));
- }
-
- public IDEService setModule(File module) {
- this.module = module;
- this.shell.append(String.format("cd \"%s\"", module.getAbsolutePath()));
- return this;
- }
-
+
@Override
public void output(CharSequence charSequence) {
- if(listener == null || charSequence == null) return;
- final String line = charSequence
- .toString()
- .replace(":showDependenciesDebug", ":startCompletionServices")
- .trim();
-
- boolean shouldOutput = true;
- if(line.contains(SHOW_DEPENDENCIES_START)) {
- shouldOutput = false;
- dependencies = new ArrayList<>();
- outputtingJars = true;
- if(listener != null)
- listener.onRunTask("> Task :app:startCompletionServices", currentTask);
- 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);
- }
- if(charSequence.toString().contains(RUN_TASK)) {
- listener.onRunTask(charSequence.toString().trim(), currentTask);
- } 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(charSequence.toString().trim(), currentTask);
- } 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(line.trim(), currentTask);
- }
- }
+ writeOut(charSequence.toString().trim() + "\n");
}
- private ArrayList dependenciesMapAsList(HashMap dependencies) {
- ArrayList result = new ArrayList<>();
- for(Map.Entry entry : dependencies.entrySet()) {
- result.add(Artifact.from(String.format("%s:%s", entry.getKey(), entry.getValue())));
- }
- return result;
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- this.instance = this;
- createShell();
- sendBroadcast(new Intent().setAction(ACTION_CREATED));
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- isRunning = true;
- return START_NOT_STICKY;
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
-
- // Delete the tmp directory. This directory is not needed anymore...
+ 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;
- }
-
- @Override
- public IBinder onBind(Intent p1) {
- return null;
- }
-
- private void createShell() {
- if(shell != null)
- shell.exit();
- shell = StudioApp.getInstance().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;
- }
- }
-
+ }
+
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 processCommands(String cmd) {
- final PreferenceManager prefs = StudioApp.getInstance().getPrefManager();
- final StringBuilder sb = new StringBuilder();
- sb.append("gradle ");
- sb.append(cmd);
- if(prefs.isStracktraceEnabled()) {
- sb.append(" --stacktrace");
+
+ 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 getArguments() {
+ final PreferenceManager prefs = app.getPrefManager();
+ final List args = new ArrayList<>();
+
+ args.add("--init-script");
+ args.add(Environment.INIT_SCRIPT.getAbsolutePath());
+
+ if (prefs.isStracktraceEnabled()) {
+ args.add("--stacktrace");
}
- if(prefs.isGradleInfoEnabled()) {
- sb.append(" --info");
+ if (prefs.isGradleInfoEnabled()) {
+ args.add("--info");
}
- if(prefs.isGradleDebugEnabled()) {
- sb.append(" --debug");
+ if (prefs.isGradleDebugEnabled()) {
+ args.add("--debug");
}
- if(prefs.isGradleScanEnabled()) {
- sb.append(" --scan");
+ if (prefs.isGradleScanEnabled()) {
+ args.add("--scan");
}
- if(prefs.isGradleWarningEnabled()) {
- sb.append(" --warning-mode all");
+ if (prefs.isGradleWarningEnabled()) {
+ args.add("--warning-mode");
+ args.add("all");
}
- return sb.toString();
+
+ return args;
}
-
+
public void execTask(GradleTask task) {
execTask(task, false);
}
-
+
public void execTask(GradleTask task, boolean stopIfRunning) {
- if(stopIfRunning && isBuilding()) {
+ if (stopIfRunning && isBuilding()) {
stopAllDaemons();
- } else if(isBuilding()) {
+ } else if (isBuilding()) {
return;
}
- if(listener != null) {
+
+ buildStart = System.currentTimeMillis();
+ if (listener != null) {
listener.saveFiles();
currentTask = task;
- listener.appendOutput(task, getString(R.string.msg_task_begin, currentTime(), task.getName()));
- shell.bgAppend(String.format("cd \"%s\"", module.getAbsolutePath()));
- shell.bgAppend(processCommands(task.getCommands()));
+ BuildParams params = new BuildParams(":" + main.name, task.getTasks(), getArguments());
+ writeMessage(Method.PROJECT_BUILD, params);
isBuilding = true;
- listener.showPreparing();
+ listener.prepare();
}
}
-
- public void showDependencies() {
- execTask(BaseGradleTasks.SHOW_DEPENDENCIES);
- }
-
- public void recreateServices() {
- execTask(BaseGradleTasks.SHOW_DEPENDENCIES, true);
- }
-
+
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() {
- if(isBuilding()) {
- forceStopDaemons();
- } else {
- shell.bgAppend("gradle --stop");
- currentTask = null;
- if(listener != null) {
- listener.appendOutput(null, getString(R.string.msg_daemons_stopped));
- }
- }
+ 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 void forceStopDaemons() {
- if(listener != null) {
- final long s = System.currentTimeMillis();
- listener.appendOutput(null, getString(R.string.msg_daemons_force_stopping));
- // Wait for a second! Just to let the user read the message!
- while(System.currentTimeMillis() - s <= 1000) {}
- }
- isBuilding = false;
- createShell();
- stopAllDaemons();
- }
-
- public static boolean isRunning() {
+
+ public boolean isRunning() {
return isRunning;
}
-
+
public boolean isBuilding() {
return isBuilding;
}
-
- public String typeString(int type) {
- switch (type) {
- case TASK_SHOW_DEPENDENCIES:
- return "";
- case TASK_ASSEMBLE_DEBUG:
- return getResources().getString(R.string.build_debug);
- case TASK_ASSEMBLE_RELEASE:
- return getResources().getString(R.string.build_release);
- case TASK_BUILD:
- return getResources().getString(R.string.build);
- case TASK_CLEAN_BUILD:
- return getResources().getString(R.string.clean_amp_build);
- case TASK_CLEAN:
- return getResources().getString(R.string.clean_project);
- case TASK_BUNDLE :
- return getResources().getString(R.string.create_aab);
- case TASK_DEX:
- return getResources().getString(R.string.compiling);
- default: return "";
- }
- }
-
+
+ public boolean isConnected() {
+ return isConnected;
+ }
+
+ 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 {
- public void onStartingGradleDaemon(GradleTask task);
- public void onRunTask(String taskName, GradleTask task);
- public void onBuildSuccessful(String msg, GradleTask task);
- public void onBuildFailed(String msg, GradleTask task);
- public void onGetDependencies(List dependencies);
- public void onGetDependenciesFailed();
- public void saveFiles();
-
- public void appendOutput(GradleTask task, CharSequence text);
- public void showPreparing();
+ 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();
}
}
diff --git a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
index ffff98528..f5cb9e0a8 100644
--- a/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
+++ b/app/src/main/java/com/itsaky/androidide/shell/ShellServer.java
@@ -3,19 +3,25 @@ package com.itsaky.androidide.shell;
import com.blankj.utilcode.util.CloseUtils;
import com.blankj.utilcode.util.ThrowableUtils;
import com.itsaky.androidide.utils.Environment;
+import com.itsaky.androidide.utils.Logger;
import java.io.BufferedReader;
import java.io.Closeable;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
+import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
public class ShellServer extends Thread {
- private final List callbacks = Collections.synchronizedList(new ArrayList());
+ private final List callbacks = Collections.synchronizedList(new ArrayList());
private BufferedReader output;
private Process process;
+
+ private static final Logger LOG = Logger.instance("ShellServer");
public ShellServer(Callback callback, String command, String dirPath) {
addCallback(callback);
@@ -30,7 +36,8 @@ public class ShellServer extends Thread {
stringBuilder.append("\nexport TMPDIR=" + Environment.path(Environment.mkdirIfNotExits(Environment.TMP_DIR)));
stringBuilder.append("\nexport JAVA_HOME=" + Environment.path(Environment.JAVA_HOME));
stringBuilder.append("\nexport ANDROID_HOME=" + Environment.path(Environment.ANDROID_HOME));
- stringBuilder.append("\nexport GRADLE_USER_HOME=$HOME/.gradle");
+ stringBuilder.append("\nexport GRADLE_INSTALLATION_DIR=" + Environment.path(Environment.GRADLE_DIR));
+ stringBuilder.append("\nexport GRADLE_USER_HOME=" + Environment.path(Environment.GRADLE_USER_HOME));
stringBuilder.append("\nexport PATH=$PATH:$HOME/bin");
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.JAVA_HOME) + "/bin");
stringBuilder.append("\nexport PATH=$PATH:" + Environment.path(Environment.GRADLE_DIR) + "/bin");
@@ -66,6 +73,24 @@ public class ShellServer extends Thread {
}
}
}
+
+ public int getPid() {
+ int pid = -1;
+ try {
+ Field f = process.getClass().getDeclaredField("pid");
+ f.setAccessible(true);
+ pid = f.getInt(process);
+ f.setAccessible(false);
+ } catch (Throwable ignored) {
+ try {
+ Matcher m = Pattern.compile("pid=(\\d+)").matcher(process.toString());
+ pid = m.find() ? Integer.parseInt(m.group(1)) : -1;
+ } catch (Throwable ignored2) {
+ pid = -1;
+ }
+ }
+ return pid;
+ }
public ShellServer addCallback(Callback callback) {
if (callback != null) {
diff --git a/app/src/main/java/com/itsaky/androidide/sockets/SocketConnection.java b/app/src/main/java/com/itsaky/androidide/sockets/SocketConnection.java
new file mode 100644
index 000000000..792b38d48
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/sockets/SocketConnection.java
@@ -0,0 +1,89 @@
+package com.itsaky.androidide.sockets;
+
+import com.blankj.utilcode.util.CloseUtils;
+import com.blankj.utilcode.util.ThrowableUtils;
+import com.google.gson.Gson;
+import com.itsaky.androidide.utils.Logger;
+import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.Socket;
+
+public abstract class SocketConnection extends Thread {
+
+ private final String host;
+ private final int port;
+ protected Socket mSocket;
+ protected BufferedOutputStream mWriter;
+
+ public static final String LOCALHOST = "localhost";
+
+ public SocketConnection(int port) {
+ this(LOCALHOST, port);
+ }
+
+ public SocketConnection(String host, int port) {
+ this.host = host;
+ this.port = port;
+ }
+
+ protected abstract void onConnected();
+ protected abstract void onDisconnected();
+ protected abstract void onFailedToConnect(Throwable exception);
+ protected abstract void onResponse(String line);
+
+ protected void write(Object msg) {
+ writeString(GSON.toJson(msg));
+ }
+
+ protected void writeString(String msg) {
+ new Thread(() -> {
+ try {
+ final String message = msg + "\n";
+ mWriter.write(message.getBytes());
+ mWriter.flush();
+ } catch (Throwable e) {
+ LOG.e("Error writing to server: " + ex(e));
+ }
+ }).start();
+ }
+
+ @Override
+ public void run() {
+ try {
+ mSocket = tryConnect();
+ onConnected();
+ final BufferedReader reader = new BufferedReader(new InputStreamReader(mSocket.getInputStream()));
+ String line;
+ while((line = reader.readLine()) != null) {
+ onResponse(line);
+ }
+ CloseUtils.closeIOQuietly(mWriter, mSocket);
+ onDisconnected();
+ } catch (Throwable e) {
+ LOG.e("Failed to connect to socket", ex(e));
+ onFailedToConnect(e);
+ }
+ }
+
+ private Socket tryConnect() {
+ Socket socket = null;
+ while(socket == null || !socket.isConnected() || mWriter == null) {
+ try {
+ socket = new Socket(host, port);
+ OutputStream os = socket.getOutputStream();
+ if(os != null)
+ mWriter = new BufferedOutputStream(os);
+ } catch (Throwable th){}
+ }
+ return socket;
+ }
+
+ protected String ex(Throwable th) {
+ return ThrowableUtils.getFullStackTrace(th);
+ }
+
+ protected static final Logger LOG = Logger.instance("SocketConnection");
+ protected static Gson GSON = new Gson();
+}
diff --git a/app/src/main/java/com/itsaky/androidide/syntax/colorschemes/SchemeAndroidIDE.java b/app/src/main/java/com/itsaky/androidide/syntax/colorschemes/SchemeAndroidIDE.java
index 186b28b31..ca1208b52 100644
--- a/app/src/main/java/com/itsaky/androidide/syntax/colorschemes/SchemeAndroidIDE.java
+++ b/app/src/main/java/com/itsaky/androidide/syntax/colorschemes/SchemeAndroidIDE.java
@@ -56,5 +56,8 @@ public class SchemeAndroidIDE extends EditorColorScheme {
setColor(DIAGNOSTIC_WARNING, 0xffFFEB3B);
setColor(DIAGNOSTIC_INFO, 0xff4CAF50);
setColor(DIAGNOSTIC_HINT, 0xffffffff);
+
+ setColor(BUILD_OUT_ERROR, 0xfff44336);
+ setColor(BUILD_OUT_SUCCESS, 0xff4CAF50);
}
}
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/GradleTask.java b/app/src/main/java/com/itsaky/androidide/tasks/GradleTask.java
index 7aa75c174..858546f2e 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/GradleTask.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/GradleTask.java
@@ -1,11 +1,13 @@
package com.itsaky.androidide.tasks;
import java.io.File;
+import java.util.List;
public interface GradleTask {
public String getName();
public String getCommands();
+ public List getTasks();
public int getTaskID();
public boolean shouldSaveFiles();
public boolean canOutput();
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/BaseGradleTasks.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/BaseGradleTasks.java
index 7c6205022..59f127e66 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/BaseGradleTasks.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/gradle/BaseGradleTasks.java
@@ -13,7 +13,6 @@ 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.plugintasks.ShowDependencies;
public class BaseGradleTasks {
@@ -31,7 +30,5 @@ public class BaseGradleTasks {
MERGE_DEX = new MergeDex(),
MERGE_EXT_DEX = new MergeExtDex(),
- MERGE_LIB_AND_PROJECT_DEX = new MergeLibAndProjectDex(),
-
- SHOW_DEPENDENCIES = new ShowDependencies();
+ MERGE_LIB_AND_PROJECT_DEX = new MergeLibAndProjectDex();
}
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeDex.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeDex.java
index cf490216b..afcfd9a7d 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeDex.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeDex.java
@@ -5,6 +5,8 @@ import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.BaseGradleTask;
import java.io.File;
+import java.util.List;
+import java.util.Arrays;
public class MergeDex extends BaseGradleTask {
@@ -18,6 +20,11 @@ public class MergeDex extends BaseGradleTask {
return "mergeDexDebug";
}
+ @Override
+ public List getTasks() {
+ return Arrays.asList("mergeDexDebug");
+ }
+
@Override
public int getTaskID() {
return IDEService.TASK_DEX;
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeExtDex.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeExtDex.java
index 1ad34d236..0ff9660ab 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeExtDex.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeExtDex.java
@@ -5,6 +5,8 @@ import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.BaseGradleTask;
import java.io.File;
+import java.util.Arrays;
+import java.util.List;
public class MergeExtDex extends BaseGradleTask {
@@ -17,6 +19,11 @@ public class MergeExtDex extends BaseGradleTask {
public String getCommands() {
return "mergeExtDexDebug";
}
+
+ @Override
+ public List getTasks() {
+ return Arrays.asList("mergeExtDexDebug");
+ }
@Override
public int getTaskID() {
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeLibAndProjectDex.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeLibAndProjectDex.java
index 1bbfce59c..dc35b7a9b 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeLibAndProjectDex.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/gradle/dex/MergeLibAndProjectDex.java
@@ -5,6 +5,8 @@ import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.BaseGradleTask;
import java.io.File;
+import java.util.Arrays;
+import java.util.List;
public class MergeLibAndProjectDex extends BaseGradleTask {
@@ -17,6 +19,11 @@ public class MergeLibAndProjectDex extends BaseGradleTask {
public String getCommands() {
return "mergeLibDexDebug mergeProjectDexDebug";
}
+
+ @Override
+ public List getTasks() {
+ return Arrays.asList("mergeLibDexDebug", "mergeProjectDexDebug");
+ }
@Override
public int getTaskID() {
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/Lint.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/Lint.java
index dc70df754..d59c97104 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/Lint.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/Lint.java
@@ -5,6 +5,8 @@ import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.BaseGradleTask;
import java.io.File;
+import java.util.Arrays;
+import java.util.List;
public class Lint extends BaseGradleTask {
@@ -17,6 +19,11 @@ public class Lint extends BaseGradleTask {
public String getCommands() {
return "lint";
}
+
+ @Override
+ public List getTasks() {
+ return Arrays.asList("lint");
+ }
@Override
public int getTaskID() {
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintDebug.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintDebug.java
index 2a720ea46..2e9671c59 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintDebug.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintDebug.java
@@ -5,6 +5,8 @@ import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.BaseGradleTask;
import java.io.File;
+import java.util.Arrays;
+import java.util.List;
public class LintDebug extends BaseGradleTask {
@@ -17,6 +19,11 @@ public class LintDebug extends BaseGradleTask {
public String getCommands() {
return "lintDebug";
}
+
+ @Override
+ public List getTasks() {
+ return Arrays.asList("lintDebug");
+ }
@Override
public int getTaskID() {
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintRelease.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintRelease.java
index a49e01436..6ac5386de 100644
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintRelease.java
+++ b/app/src/main/java/com/itsaky/androidide/tasks/gradle/lint/LintRelease.java
@@ -5,6 +5,8 @@ import com.itsaky.androidide.app.StudioApp;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.BaseGradleTask;
import java.io.File;
+import java.util.Arrays;
+import java.util.List;
public class LintRelease extends BaseGradleTask {
@@ -17,6 +19,11 @@ public class LintRelease extends BaseGradleTask {
public String getCommands() {
return "lintRelease";
}
+
+ @Override
+ public List getTasks() {
+ return Arrays.asList("lintRelease");
+ }
@Override
public int getTaskID() {
diff --git a/app/src/main/java/com/itsaky/androidide/tasks/gradle/plugintasks/ShowDependencies.java b/app/src/main/java/com/itsaky/androidide/tasks/gradle/plugintasks/ShowDependencies.java
deleted file mode 100644
index e321ae2c8..000000000
--- a/app/src/main/java/com/itsaky/androidide/tasks/gradle/plugintasks/ShowDependencies.java
+++ /dev/null
@@ -1,45 +0,0 @@
-package com.itsaky.androidide.tasks.gradle.plugintasks;
-
-import com.itsaky.androidide.app.StudioApp;
-import com.itsaky.androidide.services.IDEService;
-import com.itsaky.androidide.tasks.BaseGradleTask;
-import java.io.File;
-import com.itsaky.androidide.utils.PreferenceManager;
-
-public class ShowDependencies extends BaseGradleTask {
-
- @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 " : "") + "showDependenciesDebug";
- }
-
- @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;
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/utils/Environment.java b/app/src/main/java/com/itsaky/androidide/utils/Environment.java
index 3a168d179..7b8d5aa48 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/Environment.java
+++ b/app/src/main/java/com/itsaky/androidide/utils/Environment.java
@@ -23,13 +23,17 @@ public final class Environment {
public static File GRADLE_DIR;
public static File GRADLE_PROPS;
- public static File GRADLE_PROPS_DIR;
+ public static File GRADLE_USER_HOME;
public static File GRADLE_PLUGIN;
public static File BOOTCLASSPATH;
public static File CACHES_DIR;
public static File AAPT2;
+ public static File INIT_SCRIPT;
public static File LIBHOOKSO;
+
+ public static File GRADLE_API_DIR;
+ public static File GRADLE_API_LIBS_DIR;
public static String ANDROID_SDK_FOLDER_NAME = "android-sdk";
public static String GRADLE_FOLDER_NAME = null;
@@ -61,25 +65,24 @@ public final class Environment {
HOME = app.getRootDir();
JAVA_HOME = getJavaHome();
ANDROID_HOME = new File(HOME, ANDROID_SDK_FOLDER_NAME);
- JLS_HOME = mkdirIfNotExits(new File(HOME, "jls"));
+ JLS_HOME = mkdirIfNotExits(new File(HOME.getParentFile(), "jls"));
TMP_DIR = mkdirIfNotExits(new File(HOME, "tmp"));
GRADLE_DIR = new File(HOME, GRADLE_FOLDER_NAME);
USRDIR = mkdirIfNotExits(new File(HOME.getParentFile(), "usr"));
LIBDIR = mkdirIfNotExits(new File(USRDIR, "lib"));
BINDIR = mkdirIfNotExits(new File(USRDIR, "bin"));
+ GRADLE_API_DIR = mkdirIfNotExits(new File(HOME.getParentFile(), "gradle-api"));
+ GRADLE_API_LIBS_DIR = new File(GRADLE_API_DIR, "libs");
+
+ INIT_SCRIPT = new File(mkdirIfNotExits(new File(HOME.getParentFile(), "init")), "androidide.init.gradle");
LIBHOOKSO = new File(LIBDIR, "libhook.so");
CACHES_DIR = new File(HOME, "ide-caches");
BOOTCLASSPATH = new File("");
AAPT2 = new File(files21, "com.android.tools.build/aapt2/4.0.1-6197926");
GRADLE_PLUGIN = new File(files21, "com.itsaky.androidide/gradle");
- GRADLE_PROPS_DIR = new File(HOME, ".gradle");
- GRADLE_PROPS = new File(GRADLE_PROPS_DIR, "gradle.properties");
- }
-
- public static void updateGradleDir() {
- GRADLE_DIR = new File(HOME, GRADLE_FOLDER_NAME);
- StudioApp.getInstance().onGradleUpdated();
+ GRADLE_USER_HOME = new File(HOME, ".gradle");
+ GRADLE_PROPS = new File(GRADLE_USER_HOME, "gradle.properties");
}
public static File getGradleDir() {
diff --git a/app/src/main/java/com/itsaky/androidide/utils/PreferenceManager.java b/app/src/main/java/com/itsaky/androidide/utils/PreferenceManager.java
deleted file mode 100644
index e0a1b4b5b..000000000
--- a/app/src/main/java/com/itsaky/androidide/utils/PreferenceManager.java
+++ /dev/null
@@ -1,175 +0,0 @@
-package com.itsaky.androidide.utils;
-
-import android.annotation.SuppressLint;
-import android.content.Context;
-import android.content.SharedPreferences;
-import java.io.File;
-import java.util.HashSet;
-import java.util.Properties;
-import java.util.Set;
-import com.itsaky.androidide.app.StudioApp;
-import java.io.FileReader;
-
-public class PreferenceManager {
-
- private SharedPreferences prefs;
- private SharedPreferences.Editor editor;
-
- public static final int LOGSENDER_LATEST_VERSION = 102;
-
- public static final String KEY_FRAMEWORK_DOWNLOADED = "framework_downloaded";
- public static final String KEY_FRAMEWORK_INSTALLED = "framework_installed";
- public static final String KEY_IS_FIRST_PROJECT_BUILD = "project_isFirstBuild";
-
- public static final String KEY_EDITORFLAG_WS_LEADING = "idepref_editor_wsLeading";
- public static final String KEY_EDITORFLAG_WS_TRAILING = "idepref_editor_wsTrailing";
- public static final String KEY_EDITORFLAG_WS_INNER = "idepref_editor_wsInner";
- public static final String KEY_EDITORFLAG_WS_EMPTY_LINE = "idepref_editor_wsEmptyLine";
- public static final String KEY_EDITORFLAG_LINE_BREAK = "idepref_editor_lineBreak";
- public static final String KEY_EDITOR_DRAW_HEX = "idepref_editor_drawHexColors";
-
- public static final String KEY_GRADLE_CURRENT_DIR = "idepref_customGradle_currentFolder";
- public static final String KEY_GRADLE_FILENAMES = "idepref_customGradle_folderNames";
- public static final String KEY_GRADLECMD_STACKTRACRE = "idepref_gradleCmd_stacktrace";
- public static final String KEY_GRADLECMD_DEBUG = "idepref_gradleCmd_debug";
- public static final String KEY_GRADLECMD_SCAN = "idepref_gradleCmd_scan";
- public static final String KEY_GRADLECMD_INFO = "idepref_gradleCmd_info";
- public static final String KEY_GRADLECMD_WARNINGMODE = "idepref_gradleCmd_warningMode";
-
- public static final String KEY_LOGSENDER_CURRENT_VERSION = "logsender_currentVersion";
-
- @SuppressLint("CommitPrefEdits")
- public PreferenceManager(Context ctx) {
- this.prefs = androidx.preference.PreferenceManager.getDefaultSharedPreferences(ctx);
- this.editor = prefs.edit();
- }
-
- public PreferenceManager putInt(String key, int val) {
- editor.putInt(key, val).apply();
- return this;
- }
-
- public int getInt(String key) {
- return prefs.getInt(key, 0);
- }
-
- public int getInt(String key, int def) {
- return prefs.getInt(key, def);
- }
-
- public PreferenceManager putFloat(String key, float val) {
- editor.putFloat(key, val).apply();
- return this;
- }
-
- public float getFloat(String key) {
- return prefs.getFloat(key, 0f);
- }
-
- public float getFloat(String key, float def) {
- return prefs.getFloat(key, def);
- }
-
- public PreferenceManager putBoolean(String key, boolean value) {
- editor.putBoolean(key, value).apply();
- return this;
- }
-
- public boolean getBoolean(String key) {
- return getBoolean(key, false);
- }
-
- public boolean getBoolean(String key, boolean defaultValue) {
- return prefs.getBoolean(key, defaultValue);
- }
-
- public PreferenceManager putString(String key, String value) {
- editor.putString(key, value).apply();
- return this;
- }
-
- public String getString(String key) {
- return prefs.getString(key, null);
- }
-
- public String getString(String key, String defaultValue) {
- return prefs.getString(key, defaultValue);
- }
-
- public boolean isFrameworkDownloaded() {
- return getBoolean(KEY_FRAMEWORK_DOWNLOADED);
- }
-
- public boolean isFrameworkInstalled() {
- return isFrameworkDownloaded() && getBoolean(KEY_FRAMEWORK_INSTALLED);
- }
-
- public boolean isStracktraceEnabled() {
- return getBoolean(KEY_GRADLECMD_STACKTRACRE);
- }
-
- public boolean isGradleInfoEnabled() {
- return getBoolean(KEY_GRADLECMD_INFO, true);
- }
-
- public boolean isGradleDebugEnabled() {
- return getBoolean(KEY_GRADLECMD_DEBUG);
- }
-
- public boolean isGradleScanEnabled() {
- return getBoolean(KEY_GRADLECMD_SCAN);
- }
-
- public boolean isGradleWarningEnabled() {
- return getBoolean(KEY_GRADLECMD_WARNINGMODE);
- }
-
- public PreferenceManager setGradleStacktraceEnabled(boolean enabled) {
- return putBoolean(KEY_GRADLECMD_STACKTRACRE, enabled);
- }
-
- public PreferenceManager setGradleInfoEnabled(boolean enabled) {
- return putBoolean(KEY_GRADLECMD_INFO, enabled);
- }
-
- public PreferenceManager setGradleDebugEnabled(boolean enabled) {
- return putBoolean(KEY_GRADLECMD_DEBUG, enabled);
- }
-
- public PreferenceManager setGradleScanEnabled(boolean enabled) {
- return putBoolean(KEY_GRADLECMD_SCAN, enabled);
- }
-
- public PreferenceManager setGradleWarningEnabled(boolean enabled) {
- return putBoolean(KEY_GRADLECMD_WARNINGMODE, enabled);
- }
-
- public PreferenceManager addGradleFolder(String name) {
- Set folders = prefs.getStringSet(KEY_GRADLE_FILENAMES, new HashSet());
- if(folders == null) {
- folders = new HashSet();
- }
- if(!folders.contains(name)) {
- folders.add(name);
- }
- editor.putStringSet(KEY_GRADLE_FILENAMES, folders).apply();
- return this;
- }
-
- public Set getGradleFolderNames() {
- Set folders = prefs.getStringSet(KEY_GRADLE_FILENAMES, new HashSet());
- if(folders != null && folders.size() > 0) {
- return folders;
- } else {
- return addGradleFolder(Environment.DEFAULT_GRADLE_FOLDER).getGradleFolderNames();
- }
- }
-
- public PreferenceManager setCurentGradleFolderName(String name) {
- return addGradleFolder(name).putString(KEY_GRADLE_CURRENT_DIR, name);
- }
-
- public String getCurrentGradleFolderName() {
- return getString(KEY_GRADLE_CURRENT_DIR, Environment.DEFAULT_GRADLE_FOLDER);
- }
-}
diff --git a/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java b/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java
index 0ba1a07f9..846c668c4 100644
--- a/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java
+++ b/app/src/main/java/com/itsaky/androidide/utils/ProjectWriter.java
@@ -200,7 +200,7 @@ public class ProjectWriter {
}
}
if(success)
- notifySuccess();
+ notifySuccess(projectDir);
} else {
notifyFailed(instance.getString(R.string.failed_create_project_dir));
}
@@ -223,10 +223,10 @@ public class ProjectWriter {
});
}
- private static void notifySuccess() {
+ private static void notifySuccess(File root) {
ThreadUtils.runOnUiThread(() -> {
if(callback != null)
- callback.onSuccess();
+ callback.onSuccess(root);
});
}
diff --git a/app/src/main/java/com/itsaky/androidide/utils/TransformUtils.java b/app/src/main/java/com/itsaky/androidide/utils/TransformUtils.java
new file mode 100644
index 000000000..549983173
--- /dev/null
+++ b/app/src/main/java/com/itsaky/androidide/utils/TransformUtils.java
@@ -0,0 +1,24 @@
+package com.itsaky.androidide.utils;
+
+import android.graphics.Color;
+import android.view.View;
+import androidx.core.content.ContextCompat;
+import com.google.android.material.transition.MaterialArcMotion;
+import com.google.android.material.transition.MaterialContainerTransform;
+
+public class TransformUtils {
+
+ public static MaterialContainerTransform createContainerTransformFor(View start, View end, View drawingView) {
+ MaterialContainerTransform transform = new MaterialContainerTransform();
+ transform.setStartView(start);
+ transform.setEndView(end);
+ transform.addTarget(end);
+ transform.setDrawingViewId(drawingView.getId());
+ transform.setAllContainerColors(ContextCompat.getColor(drawingView.getContext(), com.itsaky.androidide.R.color.primaryDarkColor));
+ transform.setElevationShadowEnabled(true);
+ transform.setPathMotion(new MaterialArcMotion());
+ transform.setScrimColor(Color.TRANSPARENT);
+ return transform;
+ }
+
+}
diff --git a/app/src/main/java/io/github/rosemoe/editor/text/Content.java b/app/src/main/java/io/github/rosemoe/editor/text/Content.java
index aea033856..65cbda2cc 100644
--- a/app/src/main/java/io/github/rosemoe/editor/text/Content.java
+++ b/app/src/main/java/io/github/rosemoe/editor/text/Content.java
@@ -254,14 +254,15 @@ public class Content implements CharSequence {
*
* @param text Text to append
*/
- public void append(CharSequence text) {
+ public int append(CharSequence text) {
if(getLineCount() <= 0)
- return;
+ return 0;
final int line = getLineCount() - 1;
int col = getColumnCount(line);
if(col < 0)
col = 0;
insert(line, col, text);
+ return line;
}
/**
diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
index f72f736f8..d34eafe79 100644
--- a/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/CodeEditor.java
@@ -778,6 +778,10 @@ public class CodeEditor extends View implements ContentListener, TextAnalyzer.Ca
}
invalidate();
}
+
+ public EditorLanguage getEditorLanguage() {
+ return mLanguage;
+ }
/**
* Getter
diff --git a/app/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java b/app/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java
index d43b43667..ccd61e0b8 100644
--- a/app/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java
+++ b/app/src/main/java/io/github/rosemoe/editor/widget/EditorColorScheme.java
@@ -61,6 +61,9 @@ public class EditorColorScheme {
public static final int LOG_INFO = 32;
public static final int LOG_ERROR = 33;
public static final int LOG_WARNING = 34;
+
+ public static final int BUILD_OUT_ERROR = 53;
+ public static final int BUILD_OUT_SUCCESS = 54;
//-----------------Highlight colors-----------
@@ -126,7 +129,7 @@ public class EditorColorScheme {
/**
* Max pre-defined color id
*/
- protected static final int END_COLOR_ID = 52;
+ protected static final int END_COLOR_ID = 54;
/**
* Real color saver
*/
@@ -201,6 +204,8 @@ public class EditorColorScheme {
case OPERATOR:
color = 0xFF0066D6;
break;
+ case BUILD_OUT_SUCCESS :
+ case BUILD_OUT_ERROR :
case TEXT_NORMAL:
color = 0xFF333333;
break;
diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml
index fbd511705..55a4b1cf7 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -17,20 +17,7 @@
-
-
-
+
+
+
-
+
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/layout_main.xml b/app/src/main/res/layout/layout_main.xml
deleted file mode 100644
index de7a5a041..000000000
--- a/app/src/main/res/layout/layout_main.xml
+++ /dev/null
@@ -1,90 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/app/src/main/res/menu/menu_editor_nav.xml b/app/src/main/res/menu/menu_editor_nav.xml
index 7c1d17c6d..67e40f61a 100644
--- a/app/src/main/res/menu/menu_editor_nav.xml
+++ b/app/src/main/res/menu/menu_editor_nav.xml
@@ -2,6 +2,15 @@