Added BuildServiceHandler

Handlers now do not keep a strong reference to EditorActivity
This commit is contained in:
Akash Yadav
2021-09-23 08:58:18 +05:30
parent fa2b3758af
commit 6510b24186
7 changed files with 841 additions and 831 deletions
File diff suppressed because it is too large Load Diff
@@ -62,11 +62,7 @@ public abstract class StudioActivity extends AppCompatActivity {
public StudioApp getApp() {
return (StudioApp) getApplication();
}
public IDEService getBuildService() {
return getApp().getBuildService();
}
}
public TypedValue getTypedValueForAttr(@AttrRes int attrRes) {
TypedValue typedValue = new TypedValue();
@@ -48,7 +48,6 @@ public class StudioApp extends MultiDexApplication
{
private static StudioApp instance;
private StudioUtils mUtils;
private IDEService buildService;
private JavaLanguageServer languageServer;
private XMLCompletionService mXmlCompletionService;
private PreferenceManager mPrefsManager;
@@ -167,18 +166,6 @@ public class StudioApp extends MultiDexApplication
newShell(null).bgAppend("gradle --stop");
}
public void startBuildService(File projectRoot) {
if(getBuildService() != null) {
getBuildService().exit();
}
buildService = new IDEService(projectRoot);
}
public IDEService getBuildService() {
return buildService;
}
public void createCompletionService() {
this.mXmlCompletionService = new XMLCompletionService();
}
@@ -0,0 +1,159 @@
package com.itsaky.androidide.handlers;
import com.itsaky.androidide.EditorActivity;
import com.itsaky.androidide.R;
import com.itsaky.androidide.managers.PreferenceManager;
import com.itsaky.androidide.models.AndroidProject;
import com.itsaky.androidide.models.project.IDEModule;
import com.itsaky.androidide.models.project.IDEProject;
import com.itsaky.androidide.services.IDEService;
import com.itsaky.androidide.tasks.GradleTask;
import com.itsaky.androidide.utils.Environment;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
public class BuildServiceHandler extends IDEHandler implements IDEService.BuildListener {
private IDEService service;
public BuildServiceHandler(Provider provider) {
super(provider);
}
public IDEService getService() {
return service;
}
@Override
public void start() {
if(this.activity() == null)
throwNPE();
AndroidProject project = provider.provideAndroidProject();
if(project == null)
throwNPE();
if(project.getProjectPath() == null)
throwNPE();
this.service = new IDEService(new File(project.getProjectPath()));
this.service.setListener(this);
}
@Override
public void stop() {
this.service.stopAllDaemons();
this.service.exit();
}
@Override
public void onBuildModified() {
// Does nothing currently
}
@Override
public void onProjectLoaded(IDEProject project, Optional<IDEModule> appModule) {
project.iconPath = provider.provideAndroidProject().getIconPath();
activity().setIDEProject(project);
activity().createProjectInfoSheet(); // Recreate sheet, even if already created. Just to update its contents
if(appModule.isPresent()) {
IDEModule app = appModule.get();
activity().setStatus(activity().getString(R.string.msg_starting_completion));
final List<String> paths = new ArrayList<String>(app.dependencies);
paths.stream().filter(path -> isClasspathValid(path)).collect(Collectors.toList());
provider.provideAndroidProject().setClassPaths(paths);
File androidJar = new File(Environment.ANDROID_HOME, String.format("platforms/%s/android.jar", app.compileSdkVersion));
if(androidJar.exists()) {
Environment.setBootClasspath(androidJar);
provider.provideAndroidProject().addClasspath(androidJar.getAbsolutePath());
activity().createServices();
} else activity().setStatus("android.jar not found!");
} else activity().setStatus("Cannot get :app module...");
}
@Override
public void onStartingGradleDaemon(GradleTask task) {
activity().setStatus(activity().getString(R.string.msg_starting_daemon));
activity().getApp().setStopGradleDaemon(false);
}
@Override
public void onRunTask(GradleTask task, String name) {
if(task != null && task.canOutput()) {
activity().setStatus(service.typeString(task.getTaskID()) + " " + name);
}
}
@Override
public void onBuildSuccessful(GradleTask task, String msg) {
if(task == null) return;
if(task.canOutput())
activity().setStatus(msg);
if(task.getType() == GradleTask.Type.BUILD) {
if(task.getTaskID() == IDEService.TASK_ASSEMBLE_DEBUG) {
if(task.buildsApk()) {
activity().install(task.getApk(new File(provider.provideAndroidProject().getMainModulePath(), "build").getAbsolutePath(), provider.provideAndroidProject().getMainModule()));
}
}
}
activity().getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
activity().invalidateOptionsMenu();
}
@Override
public void onBuildFailed(GradleTask task, String msg) {
if(task == null) return;
if(task.canOutput()) {
activity().setStatus(msg);
}
activity().showViewOptions();
activity().showBuildResult();
activity().getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false);
activity().invalidateOptionsMenu();
}
@Override
public void saveFiles() {
activity().saveAll(false, false);
}
@Override
public void appendOutput(GradleTask task, CharSequence text) {
if(text == null)
return;
if(task != null && !task.canOutput()) {
return;
}
activity().appendBuildOut(text.toString());
}
@Override
public void prepare() {
boolean isFirstBuild = activity().getApp().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true);
activity().setStatus(activity().getString(isFirstBuild ? R.string.preparing_first : R.string.preparing));
if(isFirstBuild) {
activity().showFirstBuildNotice();
}
}
private boolean isClasspathValid(String path) {
if(path == null || path.trim().length() <= 0 || path.trim().equals("/")) return false;
File file = new File(path);
return file.isFile()
&& file.getName().endsWith(".jar")
/**
* Release R.jar shouldn't be included in classpath
*/
&& !file.getAbsolutePath().endsWith("/release/R.jar");
}
}
@@ -13,27 +13,23 @@ public abstract class IDEHandler {
protected static final Logger LOG = Logger.instance();
private Provider provider;
/**
* A convinient method to throw exception when trying to access a disabled handler
* >> Should only be used by subclasses <<
*/
protected void throwDisabled() {
throw new NotEnabledExeption();
protected Provider provider;
public IDEHandler(Provider provider) {
this.provider = provider;
}
protected void throwNPE () {
throw new NullPointerException();
}
public void start(Provider provider) {
this.provider = provider;
protected EditorActivity activity() {
return provider.provideEditorActivity();
}
public void stop() {
}
public abstract void start();
public abstract void stop ();
/**
* An interface to communicate between a handler and its client
@@ -68,12 +64,4 @@ public abstract class IDEHandler {
*/
JLSHandler provideJLSHandler();
}
public static class NotEnabledExeption extends RuntimeException {
@Override
public String getMessage() {
return "This hanadler is not enabled!";
}
}
}
@@ -37,13 +37,13 @@ import java.util.stream.Collectors;
public class JLSHandler extends IDEHandler implements JLSRequestor {
private EditorActivity activity;
private JavaLanguageServer languageServer;
private final LanguageClient client;
private final LanguageClientHandler client;
private static final Gson gson = new Gson();
public JLSHandler(LanguageClient client) {
public JLSHandler(LanguageClientHandler client, Provider provider) {
super(provider);
this.client = client;
}
@@ -52,10 +52,8 @@ public class JLSHandler extends IDEHandler implements JLSRequestor {
}
@Override
public void start(IDEHandler.Provider provider) {
super.start(provider);
this.activity = provider.provideEditorActivity();
if(activity == null) {
public void start() {
if(activity() == null) {
throw new NullPointerException("EditorActivity should not be null");
}
@@ -64,6 +62,8 @@ public class JLSHandler extends IDEHandler implements JLSRequestor {
throw new NullPointerException("Project is null");
}
this.client.start();
this.languageServer = new JavaLanguageServer(project, this.client);
this.languageServer.startServer();
@@ -77,68 +77,68 @@ public class JLSHandler extends IDEHandler implements JLSRequestor {
@Override
public void hideSignature() {
activity.getBinding().symbolText.setVisibility(View.GONE);
activity().getBinding().symbolText.setVisibility(View.GONE);
}
@Override
public void didOpen(DidOpenTextDocumentParams p) {
if(languageServer != null)
languageServer.didOpen(p);
else activity.addPendingMessage(activity.createMessage(JavaLanguageServer.Method.DID_OPEN, gson.toJson(p)));
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_OPEN, gson.toJson(p)));
}
@Override
public void didClose(DidCloseTextDocumentParams p) {
if(languageServer != null)
languageServer.didClose(p);
else activity.addPendingMessage(activity.createMessage(JavaLanguageServer.Method.DID_CLOSE, gson.toJson(p)));
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_CLOSE, gson.toJson(p)));
}
@Override
public void didChange(DidChangeTextDocumentParams params) {
if(languageServer != null)
languageServer.didChange(params);
else activity.addPendingMessage(activity.createMessage(JavaLanguageServer.Method.DID_CHANGE, gson.toJson(params)));
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_CHANGE, gson.toJson(params)));
}
@Override
public void didSave(DidSaveTextDocumentParams params) {
if(languageServer != null)
languageServer.didSave(params);
else activity.addPendingMessage(activity.createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
}
@Override
public void didChangeWatchedFiles(DidChangeWatchedFilesParams params) {
if(languageServer != null)
languageServer.didChangeWatchedFiles(params);
else activity.addPendingMessage(activity.createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
}
@Override
public void signatureHelp(TextDocumentPositionParams params, File file) {
if(languageServer != null)
languageServer.signatureHelp(params, file);
else activity.addPendingMessage(activity.createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
else activity().addPendingMessage(activity().createMessage(JavaLanguageServer.Method.DID_SAVE, gson.toJson(params)));
}
@Override
public void findDefinition(TextDocumentPositionParams params) {
if(languageServer == null || !languageServer.isStarted()) return;
languageServer.findDefinition(params);
activity.getProgressSheet(R.string.msg_finding_definition).show(activity.getSupportFragmentManager(), "definition_progress");
activity().getProgressSheet(R.string.msg_finding_definition).show(activity().getSupportFragmentManager(), "definition_progress");
}
@Override
public void findReferences(ReferenceParams params) {
if(languageServer == null || !languageServer.isStarted()) return;
languageServer.findReferences(params);
activity.getProgressSheet(R.string.msg_finding_references).show(activity.getSupportFragmentManager(), "references_progress");
activity().getProgressSheet(R.string.msg_finding_references).show(activity().getSupportFragmentManager(), "references_progress");
}
@Override
public void performCodeActions(final File file, List<CodeAction> actions) {
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity, R.style.AppTheme_MaterialAlertDialog);
final MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity(), R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.msg_code_actions);
builder.setItems(mapActionsAsArray(removeDuplicates(actions)), (d, w) -> {
d.dismiss();
@@ -157,9 +157,9 @@ public class JLSHandler extends IDEHandler implements JLSRequestor {
if(isInSameFile(file, action.edit.changes)) {
performCodeAction(file, action);
} else {
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity, R.style.AppTheme_MaterialAlertDialog);
MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(activity(), R.style.AppTheme_MaterialAlertDialog);
builder.setTitle(R.string.title_confirm_changes);
builder.setMessage(activity.getString(R.string.msg_confirm_changes, getFileNamesOfEdits(action.edit.changes.keySet())));
builder.setMessage(activity().getString(R.string.msg_confirm_changes, getFileNamesOfEdits(action.edit.changes.keySet())));
builder.setPositiveButton(android.R.string.yes, (d, w) -> performCodeAction(file, action));
builder.setNegativeButton(android.R.string.no, null);
builder.show();
@@ -229,8 +229,8 @@ public class JLSHandler extends IDEHandler implements JLSRequestor {
boolean wroteInFragment = false;
if(activity.getPagerAdapter() != null) {
EditorFragment frag = activity.getPagerAdapter().findEditorByFile(file);
if(activity().getPagerAdapter() != null) {
EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
if(frag != null && frag.getEditor() != null && frag.getEditor().getText() != null) {
performEdits(frag.getEditor().getText(), edits);
wroteInFragment = true;
@@ -301,7 +301,7 @@ public class JLSHandler extends IDEHandler implements JLSRequestor {
public void notifyFileCreated(File file) {
notifyExternalFileChange(file, FileChangeType.Created);
activity.openFile(file);
activity().openFile(file);
}
public void notifyFileDeleted(File file) {
@@ -39,14 +39,15 @@ import java.util.Map;
public class LanguageClientHandler extends IDEHandler implements LanguageClient {
private EditorActivity activity;
private final Map<File, List<Diagnostic>> diagnostics = new HashMap<>();
public LanguageClientHandler(Provider provider) {
super(provider);
}
@Override
public void start(IDEHandler.Provider listener) {
super.start(listener);
this.activity = listener.provideEditorActivity();
if(this.activity == null) throwNPE();
public void start() {
// Unimplemented
}
@Override
@@ -57,7 +58,7 @@ public class LanguageClientHandler extends IDEHandler implements LanguageClient
@Override
public void publishDiagnostics(PublishDiagnosticsParams params) {
boolean error = params == null || params.diagnostics == null || params.diagnostics.size() <= 0;
activity.handleDiagnosticsResultVisibility(error);
activity().handleDiagnosticsResultVisibility(error);
if(error) return;
@@ -65,32 +66,32 @@ public class LanguageClientHandler extends IDEHandler implements LanguageClient
if(!(file.exists() && file.isFile())) return;
diagnostics.put(file, params.diagnostics);
activity.getDiagnosticsList().setAdapter(newDiagnosticsAdapter());
activity().getDiagnosticsList().setAdapter(newDiagnosticsAdapter());
EditorFragment editor = null;
if(activity.getPagerAdapter() != null && (editor = activity.getPagerAdapter().findEditorByFile(new File(params.uri))) != null) {
if(activity().getPagerAdapter() != null && (editor = activity().getPagerAdapter().findEditorByFile(new File(params.uri))) != null) {
editor.setDiagnostics(params.diagnostics);
}
}
@Override
public void gotoDefinition(List<Location> locations) {
if(activity.getProgressSheet() != null && activity.getProgressSheet().isShowing())
activity.getProgressSheet().dismiss();
if(activity().getProgressSheet() != null && activity().getProgressSheet().isShowing())
activity().getProgressSheet().dismiss();
if(locations == null || locations.size() <= 0) {
activity.getApp().toast(R.string.msg_no_definition, Toaster.Type.ERROR);
activity().getApp().toast(R.string.msg_no_definition, Toaster.Type.ERROR);
return;
}
Location loc = locations.get(0);
final File file = new File(loc.uri);
final Range range = loc.range;
try {
if(activity.getPagerAdapter() == null) return;
if(activity.getPagerAdapter().getCount() <= 0) {
activity.openFile(file, range);
if(activity().getPagerAdapter() == null) return;
if(activity().getPagerAdapter().getCount() <= 0) {
activity().openFile(file, range);
return;
}
EditorFragment frag = activity.getPagerAdapter().getFrag(activity.getBinding().tabs.getSelectedTabPosition());
EditorFragment frag = activity().getPagerAdapter().getFrag(activity().getBinding().tabs.getSelectedTabPosition());
if(frag != null
&& frag.getFile() != null
&& frag.getEditor() != null
@@ -101,7 +102,7 @@ public class LanguageClientHandler extends IDEHandler implements LanguageClient
frag.getEditor().setSelectionRegion(range.start.line, range.start.column, range.end.line, range.end.column);
}
} else {
activity.openFileAndSelect(file, range);
activity().openFileAndSelect(file, range);
}
} catch (Throwable th) {
LOG.error(ThrowableUtils.getFullStackTrace(th));
@@ -110,16 +111,16 @@ public class LanguageClientHandler extends IDEHandler implements LanguageClient
@Override
public void references(List<Location> references) {
if(activity.getProgressSheet() != null && activity.getProgressSheet().isShowing())
activity.getProgressSheet().dismiss();
if(activity().getProgressSheet() != null && activity().getProgressSheet().isShowing())
activity().getProgressSheet().dismiss();
boolean error = references == null || references.size() <= 0;
activity.handleSearchResultVisibility(error);
activity().handleSearchResultVisibility(error);
if(error) {
activity.getApp().toast(R.string.msg_no_references, Toaster.Type.INFO);
activity.getSearchResultList().setAdapter(new SearchListAdapter(null, null, null));
activity().getApp().toast(R.string.msg_no_references, Toaster.Type.INFO);
activity().getSearchResultList().setAdapter(new SearchListAdapter(null, null, null));
return;
}
@@ -130,7 +131,7 @@ public class LanguageClientHandler extends IDEHandler implements LanguageClient
if(loc == null || loc.uri == null || loc.range == null) continue;
final File file = new File(loc.uri);
if(!file.exists() || !file.isFile()) continue;
EditorFragment frag = activity.getPagerAdapter().findEditorByFile(file);
EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
Content content;
if(frag != null && frag.getEditor() != null)
content = frag.getEditor().getText();
@@ -155,33 +156,33 @@ public class LanguageClientHandler extends IDEHandler implements LanguageClient
}
}
activity.handleSearchResults(results);
activity().handleSearchResults(results);
}
@Override
public void signatureHelp(SignatureHelp signature, File file) {
if(signature == null || signature.signatures == null) {
activity.getBinding().symbolText.setVisibility(View.GONE);
activity().getBinding().symbolText.setVisibility(View.GONE);
return;
}
SignatureInformation info = signature.signatures.get(signature.activeSignature);
activity.getBinding().symbolText.setText(formatSignature(info, signature.activeParameter));
final EditorFragment frag = activity.getPagerAdapter().findEditorByFile(file);
activity().getBinding().symbolText.setText(formatSignature(info, signature.activeParameter));
final EditorFragment frag = activity().getPagerAdapter().findEditorByFile(file);
if(frag != null) {
final CodeEditor editor = frag.getEditor();
final float[] cursor = editor.getCursorPosition();
float x = editor.updateCursorAnchor() - (activity.getBinding().symbolText.getWidth() / 2);
float y = activity.getBinding().editorAppBarLayout.getHeight() + (cursor[0] - editor.getRowHeight() - editor.getOffsetY() - activity.getBinding().symbolText.getHeight());
activity.getBinding().symbolText.setVisibility(View.VISIBLE);
activity.positionViewWithinScreen(activity.getBinding().symbolText, x, y);
float x = editor.updateCursorAnchor() - (activity().getBinding().symbolText.getWidth() / 2);
float y = activity().getBinding().editorAppBarLayout.getHeight() + (cursor[0] - editor.getRowHeight() - editor.getOffsetY() - activity().getBinding().symbolText.getHeight());
activity().getBinding().symbolText.setVisibility(View.VISIBLE);
activity().positionViewWithinScreen(activity().getBinding().symbolText, x, y);
}
}
@Override
public void javaColors(JavaColors colors) {
final File file = new File(colors.uri);
final EditorFragment editor = this.activity.getPagerAdapter().findEditorByFile(file);
final EditorFragment editor = this.activity().getPagerAdapter().findEditorByFile(file);
if(editor != null)
editor.setJavaColors(colors);
}
@@ -189,16 +190,16 @@ public class LanguageClientHandler extends IDEHandler implements LanguageClient
@Override
public void onServerStarted(int currentId) {
new Thread(() -> {
final JavaLanguageServer server = activity.getApp().getJavaLanguageServer();
while(server != null && !activity.pendingMessages.isEmpty()) {
Message msg = activity.pendingMessages.pop();
final JavaLanguageServer server = provider.provideJLSHandler().getJLS();
while(server != null && !activity().pendingMessages.isEmpty()) {
Message msg = activity().pendingMessages.pop();
server.send(msg);
}
}).start();
}
public DiagnosticsAdapter newDiagnosticsAdapter() {
return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity);
return new DiagnosticsAdapter(mapAsGroup(this.diagnostics), activity());
}
/**