diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index aa57e6f9c..9ed96ad70 100644 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -49,6 +49,7 @@ import com.itsaky.androidide.fragments.sheets.OptionsListFragment; import com.itsaky.androidide.fragments.sheets.ProgressSheet; import com.itsaky.androidide.fragments.sheets.ProjectInfoSheet; import com.itsaky.androidide.fragments.sheets.TextSheetFragment; +import com.itsaky.androidide.handlers.BuildServiceHandler; import com.itsaky.androidide.handlers.IDEHandler; import com.itsaky.androidide.handlers.jls.JLSHandler; import com.itsaky.androidide.handlers.jls.LanguageClientHandler; @@ -102,7 +103,6 @@ import me.piruin.quickaction.ActionItem; import me.piruin.quickaction.QuickAction; public class EditorActivity extends StudioActivity implements FileTreeFragment.FileActionListener, - IDEService.BuildListener, TabLayout.OnTabSelectedListener, NavigationView.OnNavigationItemSelectedListener, DiagnosticClickListener, @@ -126,6 +126,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F private JLSHandler mJLSHandler; private LanguageClientHandler mLanguageClientHandler; + private BuildServiceHandler mBuildServiceHandler; private LogLanguageImpl mLogLanguageImpl; @@ -206,14 +207,12 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F createQuickActions(); - mLanguageClientHandler = new LanguageClientHandler(); - mJLSHandler = new JLSHandler(mLanguageClientHandler); + mLanguageClientHandler = new LanguageClientHandler(this); + mBuildServiceHandler = new BuildServiceHandler(this); + mJLSHandler = new JLSHandler(mLanguageClientHandler, this); - getApp().checkAndUpdateGradle(); - getApp().startBuildService(new File(mProject.getProjectPath())); - getApp().getBuildService().setListener(this); + getApp().checkAndUpdateGradle(); startServices(); - invalidateOptionsMenu(); KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged()); registerLogReceiver(); @@ -276,73 +275,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F handleDiagnosticsResultVisibility(true); handleSearchResultVisibility(true); } - - private void startServices() { - getBuildService().assembleDebug(false); - } - - private void removeFromParent(View v) { - if(v.getParent() != null && v.getParent() instanceof ViewGroup) { - ((ViewGroup) v.getParent()).removeView(v); - } - } - - private void onSoftInputChanged() { - invalidateOptionsMenu(); - if(KeyboardUtils.isSoftInputVisible(this)) { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.TOP)); - symbolInput.setVisibility(View.VISIBLE); - mBinding.fabView.hide(); - } else { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.BOTTOM)); - symbolInput.setVisibility(View.GONE); - mBinding.fabView.show(); - } - } - - private void refreshSymbolInput(EditorFragment frag) { - symbolInput.bindEditor(frag.getEditor()); - symbolInput.setSymbols(Symbols.forFile(frag.getFile())); - } - - private void notifySyncNeeded() { - if(getBuildService() != null && !getBuildService().isBuilding()) { - getSyncBanner() - .setNegative(android.R.string.cancel, null) - .setPositive(android.R.string.ok, v -> { - getBuildService().assembleDebug(false); - }) - .show(); - } - } - - private void closeProject(boolean manualFinish) { - if (getBuildService() != null) { - getBuildService().setListener(null); - getBuildService().exit(); - } - - getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT); - - mJLSHandler.stop(); - - 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() { @@ -533,12 +465,616 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F public IDEProject provideIDEProject() { return mIDEProject; } - + @Override public JLSHandler provideJLSHandler() { return mJLSHandler; } + public void handleSearchResults(Map> results) { + getSearchResultList().setAdapter(new com.itsaky.androidide.adapters.SearchListAdapter(results, file -> { + openFile(file); + hideSearchResults(); + }, match -> { + openFileAndSelect(match.file, match); + hideSearchResults(); + })); + mBinding.transformScrim.setVisibility(View.VISIBLE); + mBinding.fabView.setVisibility(View.GONE); + showSearchResults(); + + if(mSearchingProgress != null && mSearchingProgress.isShowing()) + mSearchingProgress.dismiss(); + } + + public void appendApkLog(LogLine line) { + getLogLanguage().addLine(line); + appendLogOut(line.toString()); + } + + public void setIDEProject (IDEProject project) { + this.mIDEProject = project; + } + + public void appendBuildOut(final String str) { + runOnUiThread(() -> { + String strFinal = str.endsWith("\n") ? str : str.concat("\n"); + getBuildView(true).getText().append(strFinal); + }); + } + + public void appendLogOut(final String str) { + runOnUiThread(() -> { + String strFinal = str.endsWith("\n") ? str : str.concat("\n"); + getLogView(true).getText().append(strFinal); + }); + } + + public void showFiles() { + if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) { + hideViewOptions(); + } + + mBinding.getRoot().openDrawer(GravityCompat.END, true); + } + + public void showDaemonStatus() { + if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) { + hideViewOptions(); + } + ShellServer shell = getApp().newShell(t -> getDaemonStatusFragment().append(t.toString())); + shell.bgAppend(String.format("echo '%s'", getString(R.string.msg_getting_daemom_status))); + shell.bgAppend("gradle --status"); + if(!getDaemonStatusFragment().isShowing()) + getDaemonStatusFragment().show(getSupportFragmentManager(), "daemon_status"); + } + + public void hideToolbarAndTabs() { + mBinding.editorAppBarLayout.setVisibility(View.INVISIBLE); + } + + public void showToolbarAndTabs() { + mBinding.editorAppBarLayout.setVisibility(View.VISIBLE); + } + + public void showViewOptions() { + try { + EditorFragment frag = mPagerAdapter.getFrag(mBinding.tabs.getSelectedTabPosition()); + if(frag != null && frag.getEditor() != null) { + frag.getEditor().hideAutoCompleteWindow(); + frag.getEditor().hideDiagnosticWindow(); + } + } catch (Throwable e) {} + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.fabView, mBinding.viewOptionsCard)); + mBinding.viewOptionsCard.setVisibility(View.VISIBLE); + mBinding.transformScrim.setVisibility(View.VISIBLE); + mBinding.fabView.setVisibility(View.GONE); + } + + public void hideViewOptions() { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewOptionsCard, mBinding.fabView)); + mBinding.viewOptionsCard.setVisibility(View.GONE); + mBinding.transformScrim.setVisibility(View.GONE); + mBinding.fabView.setVisibility(View.VISIBLE); + } + + public void showBuildResult() { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewBuildOut, mBinding.buildContainer)); + mBinding.buildContainer.setVisibility(View.VISIBLE); + mBinding.viewOptionsCard.setVisibility(View.GONE); + hideToolbarAndTabs(); + } + + public void hideBuildResult() { + showToolbarAndTabs(); + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.buildContainer, mBinding.viewBuildOut)); + mBinding.buildContainer.setVisibility(View.GONE); + mBinding.viewOptionsCard.setVisibility(View.VISIBLE); + } + + public void showLogResult() { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewLogs, mBinding.logContainer)); + mBinding.logContainer.setVisibility(View.VISIBLE); + mBinding.viewOptionsCard.setVisibility(View.GONE); + hideToolbarAndTabs(); + } + + public void hideLogResult() { + showToolbarAndTabs(); + + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.logContainer, mBinding.viewLogs)); + mBinding.logContainer.setVisibility(View.GONE); + mBinding.viewOptionsCard.setVisibility(View.VISIBLE); + } + + public void showDiagnostics() { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewDiags, mBinding.diagContainer)); + mBinding.diagContainer.setVisibility(View.VISIBLE); + mBinding.viewOptionsCard.setVisibility(View.GONE); + hideToolbarAndTabs(); + } + + public void hideDiagnostics() { + showToolbarAndTabs(); + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.diagContainer, mBinding.viewDiags)); + mBinding.diagContainer.setVisibility(View.GONE); + mBinding.viewOptionsCard.setVisibility(View.VISIBLE); + } + + public void showSearchResults() { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewSearchResults, mBinding.searchResultsContainer)); + mBinding.searchResultsContainer.setVisibility(View.VISIBLE); + mBinding.viewOptionsCard.setVisibility(View.GONE); + hideToolbarAndTabs(); + } + + public void hideSearchResults() { + showToolbarAndTabs(); + TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.searchResultsContainer, mBinding.viewSearchResults)); + mBinding.searchResultsContainer.setVisibility(View.GONE); + mBinding.viewOptionsCard.setVisibility(View.VISIBLE); + } + + public void handleDiagnosticsResultVisibility(boolean errorVisible) { + mBinding.diagEmptyView.setVisibility(errorVisible ? View.VISIBLE : View.GONE); + getDiagnosticsList().setVisibility(errorVisible ? View.GONE : View.VISIBLE); + } + + public void handleSearchResultVisibility(boolean errorVisible) { + mBinding.searchEmptyView.setVisibility(errorVisible ? View.VISIBLE : View.GONE); + getSearchResultList().setVisibility(errorVisible ? View.GONE : View.VISIBLE); + } + + public void setStatus(final CharSequence text) { + try { + runOnUiThread(() -> mBinding.editorStatusText.setText(text)); + } catch (Throwable th) {} + } + + public void showFirstBuildNotice() { + new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog) + .setPositiveButton(android.R.string.ok, null) + .setTitle(R.string.title_first_build) + .setMessage(R.string.msg_first_build) + .setCancelable(false) + .create().show(); + } + + @Override + public void onGroupClick(DiagnosticGroup group) { + if(group != null + && group.file != null + && group.file.exists() + && FileUtils.isUtf8(group.file)) + { + openFile(group.file); + } + } + + @Override + public void onDiagnosticClick(File file, Diagnostic diagnostic) { + openFileAndSelect(file, diagnostic.range); + hideDiagnostics(); + } + + public EditorPagerAdapter getPagerAdapter(){ + return mPagerAdapter; + } + + public ActivityEditorBinding getBinding() { + return mBinding; + } + + /** + * Positions the view to the provided coordinates and within screen + * + * @param view View to position + * @param initialX Initial X coordinate of view + * @param initialY Initial Y coordinate of view + */ + public void positionViewWithinScreen(View view, float initialX, float initialY) { + view.setX(initialX); + view.setY(initialY); + + Rect r = new Rect(); + int width = view.getWidth(); + initialX = view.getX(); + view.getWindowVisibleDisplayFrame(r); + if (r.width() != width) { + + /** + * Will be true when the view is going out of screen to left + */ + if (initialX < r.left) { + initialX = SizeUtils.dp2px(8); // an offset of 8dp from the left edge of screen + view.setX(initialX); + } + + /** + * Will be true when the view is going out of screen to right + */ + if (initialX + width > r.right) { + initialX = r.right - SizeUtils.dp2px(8) - width; // position to the right but leaving 8dp space from the right edge of screen + view.setX(initialX); + } + } + } + + public void openFileAndSelect(File file, Range range) { + openFile(file, range); + EditorFragment opened = mPagerAdapter.findEditorByFile(file); + if (opened != null && opened.getEditor() != null) { + CodeEditor editor = opened.getEditor(); + editor.post(() -> { + if (range.start.equals(range.end)) { + editor.setSelection(range.start.line, range.start.column); + } else { + editor.setSelectionRegion(range.start.line, range.start.column, range.end.line, range.end.column); + } + }); + } + } + + public void addPendingMessage(Message msg) { + pendingMessages.push(msg); + } + + public Message createMessage(String method, String data) { + Message msg = new Message(); + msg.jsonrpc = "2.0"; + msg.method = method; + msg.params = new JsonParser().parse(data); + return msg; + } + + /***************************** + **** Tab Related ************ + *****************************/ + + @Override + public void onTabSelected(TabLayout.Tab p1) { + EditorFragment current = mPagerAdapter.getFrag(p1.getPosition()); + if(current != null && current.getFile() != null) { + this.mCurrentFragment = current; + this.mCurrentFile = current.getFile(); + refreshSymbolInput(current); + } + + invalidateOptionsMenu(); + } + + @Override + public void onTabUnselected(TabLayout.Tab p1) { + EditorFragment frag = mPagerAdapter.getFrag(p1.getPosition()); + if(frag == null) return; + boolean isGradle = frag.isModified() && frag.getFile().getName().endsWith(EditorFragment.EXT_GRADLE); + frag.save(); + if(isGradle) { + notifySyncNeeded(); + } + } + + @Override + public void onTabReselected(TabLayout.Tab p1) { + mTabCloseAction.show(mBinding.tabs); + } + + @Override + public boolean onNavigationItemSelected(MenuItem p1) { + final int id = p1.getItemId(); + if(id == R.id.editornav_discuss) { + getApp().openTelegramGroup(); + } else if(id == R.id.editornav_suggest) { + getApp().openIssueTracker(); + } else if(id == R.id.editornav_needHelp) { + showNeedHelpDialog(); + } else if(id == R.id.editornav_settings) { + 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; + } + + public void loadFragment(Fragment fragment) { + super.loadFragment(fragment, mBinding.editorFrameLayout.getId()); + } + + public ProjectInfoSheet getProjectInfoSheet() { + return mProjectInfoSheet == null ? createProjectInfoSheet() : mProjectInfoSheet; + } + + public ProjectInfoSheet createProjectInfoSheet() { + mProjectInfoSheet = new ProjectInfoSheet(); + mProjectInfoSheet.setProject(mIDEProject); + return mProjectInfoSheet; + } + + public OptionsListFragment getFileOptionsFragment(File file) { + mFileOptionsFragment = new OptionsListFragment(); + mFileOptionsFragment.addOption(new SheetOption(0, R.drawable.ic_file_copy_path, R.string.copy_path, file)); + mFileOptionsFragment.addOption(new SheetOption(1, R.drawable.ic_file_rename, R.string.rename_file, file)); + mFileOptionsFragment.addOption(new SheetOption(2, R.drawable.ic_file_delete, R.string.delete_file, file)); + if(file.isDirectory()) { + mFileOptionsFragment.addOption(new SheetOption(3, R.drawable.ic_new_file, R.string.new_file, file)); + mFileOptionsFragment.addOption(new SheetOption(4, R.drawable.ic_new_folder, R.string.new_folder, file)); + } + mFileOptionsFragment.setOnOptionsClickListener(o -> handleOptionClick(o)); + return mFileOptionsFragment; + } + + public ProgressSheet getProgressSheet() { + return mSearchingProgress; + } + + public ProgressSheet getProgressSheet(int msg) { + if(mSearchingProgress != null && mSearchingProgress.isShowing()) { + mSearchingProgress.dismiss(); + } + mSearchingProgress = new ProgressSheet(); + mSearchingProgress.setCancelable(false); + mSearchingProgress.setWelcomeTextEnabled(false); + mSearchingProgress.setMessage(getString(msg)); + mSearchingProgress.setSubMessageEnabled(false); + return mSearchingProgress; + } + + public MaterialBanner getSyncBanner() { + return mBinding.syncBanner.setContentTextColor(ContextCompat.getColor(this, R.color.primaryTextColor)) + .setBannerBackgroundColor(ContextCompat.getColor(this, R.color.primaryLightColor)) + .setButtonTextColor(ContextCompat.getColor(this, R.color.secondaryColor)) + .setIcon(R.drawable.ic_sync) + .setContentText(R.string.msg_sync_needed); + } + + public TextSheetFragment getDaemonStatusFragment() { + return mDaemonStatusFragment == null ? mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true).setTitleText(R.string.gradle_daemon_status) : mDaemonStatusFragment; + } + + public CodeEditor getBuildView() { + return getBuildView(false); + } + + public CodeEditor getBuildView(boolean setup) { + CodeEditor edit = buildView == null ? createBuildView() : buildView; + if(setup && edit.getParent() == null) { + setupContainers(); + } + return edit; + } + + public CodeEditor createBuildView() { + buildView = new CodeEditor(this); + buildView.setEditable(false); + buildView.setDividerWidth(0); + buildView.setEditorLanguage(new BuildOutputLanguage()); + buildView.setOverScrollEnabled(false); + buildView.setTextActionMode(CodeEditor.TextActionMode.ACTION_MODE); + buildView.setWordwrap(false); + buildView.setUndoEnabled(false); + buildView.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono()); + buildView.setTypefaceText(TypefaceUtils.jetbrainsMono()); + buildView.setTextSize(12); + buildView.setColorScheme(new SchemeAndroidIDE()); + return buildView; + } + + public CodeEditor getLogView() { + return getLogView(false); + } + + public CodeEditor getLogView(boolean setup) { + CodeEditor edit = logView == null ? createLogView() : logView; + if(setup && edit.getParent() == null) { + setupContainers(); + } + return edit; + } + + public CodeEditor createLogView() { + logView = new CodeEditor(this); + logView.setEditable(false); + logView.setDividerWidth(0); + logView.setEditorLanguage(getLogLanguage()); + logView.setOverScrollEnabled(false); + logView.setTextActionMode(CodeEditor.TextActionMode.ACTION_MODE); + logView.setWordwrap(false); + logView.setUndoEnabled(false); + logView.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono()); + logView.setTypefaceText(TypefaceUtils.jetbrainsMono()); + logView.setTextSize(12); + buildView.setColorScheme(new SchemeAndroidIDE()); + return logView; + } + + public RecyclerView getDiagnosticsList() { + return diagnosticList == null ? createDiagnosticsList() : diagnosticList; + } + + public RecyclerView createDiagnosticsList() { + diagnosticList = new RecyclerView(this); + diagnosticList.setLayoutManager(new LinearLayoutManager(this)); + diagnosticList.setAdapter(mLanguageClientHandler.newDiagnosticsAdapter()); + return diagnosticList; + } + + public RecyclerView getSearchResultList() { + return searchResultList == null ? createSearchResultList() : searchResultList; + } + + public RecyclerView createSearchResultList() { + searchResultList = new RecyclerView(this); + searchResultList.setLayoutManager(new LinearLayoutManager(this)); + return searchResultList; + } + + public LogLanguageImpl getLogLanguage() { + return mLogLanguageImpl == null ? mLogLanguageImpl = new LogLanguageImpl() : mLogLanguageImpl; + } + + @Override + public void openFile(File file) { + openFile(file, null); + } + + public EditorFragment openFile(File file, Range selection) { + if(selection == null) selection = Range.ofZero(); + int i = mPagerAdapter.openFile(file, selection, this, mJLSHandler); + if(i >= 0 && !mBinding.tabs.getTabAt(i).isSelected()) + mBinding.tabs.getTabAt(i).select(); + mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END); + invalidateOptionsMenu(); + try { + return mPagerAdapter.getFrag(i); + } catch (Throwable th) { + return null; + } + } + + @Override + public void onOpenSuccessful(File file, String text) { + if(file.getName().endsWith(".java")) { + TextDocumentItem doc = new TextDocumentItem(); + doc.languageId = "java"; + doc.uri = file.toURI(); + doc.text = text; + doc.version = VersionedFileManager.fileOpened(file); + DidOpenTextDocumentParams p = new DidOpenTextDocumentParams(); + p.textDocument = doc; + mJLSHandler.didOpen(p); + } + } + + @Override + public void showFileOptions(File thisFile, TreeNode node) { + mLastHolded = node; + getFileOptionsFragment(thisFile).show(getSupportFragmentManager(), TAG_FILE_OPTIONS_FRAGMENT); + } + + public boolean saveAll() { + return saveAll(true); + } + + public boolean saveAll(boolean notify) { + return saveAll(notify, false); + } + + public boolean saveAll(boolean notify, boolean canProcessResources) { + SaveResult result = mPagerAdapter.saveAll(); + if(notify) { + getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS); + } + if(result.gradleSaved){ + notifySyncNeeded(); + } + if(result.xmlSaved && canProcessResources && getBuildService() != null) { + getBuildService().updateResourceClasses(); + } + return result.gradleSaved; + } + + public void install(File apk) { + if(apk.exists()) { + Intent i = IntentUtils.getInstallAppIntent(apk); + if(i != null) { + startActivity(i); + } + } + } + + public void createServices() { + new TaskExecutor().executeAsync(() -> { + getApp().createCompletionService(); + mJLSHandler.start(); + return null; + }, __ -> { + setStatus(getString(getApp().areCompletorsStarted() ? R.string.msg_service_started : R.string.msg_starting_completion_failed)); + }); + } + + ///////////////////////////////////////////////// + ////////////// PRIVATE APIS ///////////////////// + ///////////////////////////////////////////////// + + private MaterialContainerTransform createContainerTransformFor(View start, View end) { + return createContainerTransformFor(start, end, mBinding.realContainer); + } + + private MaterialContainerTransform createContainerTransformFor(View start, View end, View drawingView) { + return TransformUtils.createContainerTransformFor(start, end, drawingView); + } + + private void startServices() { + mBuildServiceHandler.start(); + getBuildService().assembleDebug(false); + } + + private void removeFromParent(View v) { + if(v.getParent() != null && v.getParent() instanceof ViewGroup) { + ((ViewGroup) v.getParent()).removeView(v); + } + } + + private void onSoftInputChanged() { + invalidateOptionsMenu(); + if(KeyboardUtils.isSoftInputVisible(this)) { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.TOP)); + symbolInput.setVisibility(View.VISIBLE); + mBinding.fabView.hide(); + } else { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.BOTTOM)); + symbolInput.setVisibility(View.GONE); + mBinding.fabView.show(); + } + } + + private void refreshSymbolInput(EditorFragment frag) { + symbolInput.bindEditor(frag.getEditor()); + symbolInput.setSymbols(Symbols.forFile(frag.getFile())); + } + + private void notifySyncNeeded() { + if(getBuildService() != null && !getBuildService().isBuilding()) { + getSyncBanner() + .setNegative(android.R.string.cancel, null) + .setPositive(android.R.string.ok, v -> { + getBuildService().assembleDebug(false); + }) + .show(); + } + } + + private void closeProject(boolean manualFinish) { + if (getBuildService() != null) { + getBuildService().setListener(null); + getBuildService().exit(); + } + + getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT); + + mJLSHandler.stop(); + + 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(); + } + private AlertDialog getFindInProjectDialog() { return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog; } @@ -613,464 +1149,17 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F mFindInProjectDialog = builder.create(); return mFindInProjectDialog; } - - public void handleSearchResults(Map> results) { - getSearchResultList().setAdapter(new SearchListAdapter(results, file -> { - openFile(file); - hideSearchResults(); - }, match -> { - openFileAndSelect(match.file, match); - hideSearchResults(); - })); - mBinding.transformScrim.setVisibility(View.VISIBLE); - mBinding.fabView.setVisibility(View.GONE); - showSearchResults(); - - if(mSearchingProgress != null && mSearchingProgress.isShowing()) - mSearchingProgress.dismiss(); - } - - /************************************ - * Build Related * - ************************************/ - - private void appendApkLog(LogLine line) { - getLogLanguage().addLine(line); - appendLogOut(line.toString()); - } - - @Override - public void appendOutput(GradleTask task, CharSequence text) { - if(text == null) - return; - if(task != null && !task.canOutput()) { - return; - } - appendBuildOut(text.toString()); - } - - @Override - public void prepare() { - boolean isFirstBuild = getApp().getPrefManager().getBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, true); - setStatus(getString(isFirstBuild ? R.string.preparing_first : R.string.preparing)); - if(isFirstBuild) { - showFirstBuildNotice(); - } - } - - @Override - public void onStartingGradleDaemon(GradleTask task) { - setStatus(getString(R.string.msg_starting_daemon)); - getApp().setStopGradleDaemon(false); - } - - @Override - public void onRunTask(GradleTask task, String taskName) { - if(task != null && task.canOutput()) { - setStatus(getBuildService().typeString(task.getTaskID()) + " " + taskName); - } - } - - @Override - public void onBuildSuccessful(GradleTask task, String msg) { - if(task == null) return; - if(task.canOutput()) - setStatus(msg); - - if(task.getType() == GradleTask.Type.BUILD) { - if(task.getTaskID() == IDEService.TASK_ASSEMBLE_DEBUG) { - if(task.buildsApk()) { - install(task.getApk(new File(mProject.getMainModulePath(), "build").getAbsolutePath(), mProject.getMainModule())); - } - } - } - - getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false); - - invalidateOptionsMenu(); - } - - @Override - public void onBuildFailed(GradleTask task, String msg) { - if(task == null) return; - if(task.canOutput()) { - setStatus(msg); - } - - showBuildResult(); - - getApp().getPrefManager().putBoolean(PreferenceManager.KEY_IS_FIRST_PROJECT_BUILD, false); - - invalidateOptionsMenu(); - } - - @Override - public void onProjectLoaded(IDEProject project, Optional appModule) { - project.iconPath = mProject.getIconPath(); - mIDEProject = project; - createProjectInfoSheet(); // Recreate sheet, even if already created. Just to update its contents - if(appModule.isPresent()) { - IDEModule app = appModule.get(); - setStatus(getString(R.string.msg_starting_completion)); - - final List paths = new ArrayList(app.dependencies); - paths.stream().filter(path -> isClasspathValid(path)).collect(Collectors.toList()); - - mProject.setClassPaths(paths); - - File androidJar = new File(Environment.ANDROID_HOME, String.format("platforms/%s/android.jar", app.compileSdkVersion)); - if(androidJar.exists()) { - Environment.setBootClasspath(androidJar); - mProject.addClasspath(androidJar.getAbsolutePath()); - createServices(); - } else setStatus("android.jar not found!"); - } else setStatus("Cannot get :app module..."); - } - - @Override - public void onBuildModified() { - // Does nothing currently - } - - 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"); - } - - @Override - public void saveFiles() { - saveAll(false, false); - } - - // Can be called from a different different, better to run on UI thread - private void appendBuildOut(final String str) { - runOnUiThread(() -> { - String strFinal = str.endsWith("\n") ? str : str.concat("\n"); - getBuildView(true).getText().append(strFinal); - }); - } - - // Can be called from a different different, better to run on UI thread - private void appendLogOut(final String str) { - runOnUiThread(() -> { - String strFinal = str.endsWith("\n") ? str : str.concat("\n"); - getLogView(true).getText().append(strFinal); - }); - } - - private void showFiles() { - if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) { - hideViewOptions(); - } - - mBinding.getRoot().openDrawer(GravityCompat.END, true); - } - - private void showDaemonStatus() { - if(mBinding.viewOptionsCard.getVisibility() == View.VISIBLE) { - hideViewOptions(); - } - ShellServer shell = getApp().newShell(t -> getDaemonStatusFragment().append(t.toString())); - shell.bgAppend(String.format("echo '%s'", getString(R.string.msg_getting_daemom_status))); - shell.bgAppend("gradle --status"); - if(!getDaemonStatusFragment().isShowing()) - getDaemonStatusFragment().show(getSupportFragmentManager(), "daemon_status"); - } - - private void hideToolbarAndTabs() { - mBinding.editorAppBarLayout.setVisibility(View.INVISIBLE); - } - - private void showToolbarAndTabs() { - mBinding.editorAppBarLayout.setVisibility(View.VISIBLE); - } - - private void showViewOptions() { - try { - EditorFragment frag = mPagerAdapter.getFrag(mBinding.tabs.getSelectedTabPosition()); - if(frag != null && frag.getEditor() != null) { - frag.getEditor().hideAutoCompleteWindow(); - frag.getEditor().hideDiagnosticWindow(); - } - } catch (Throwable e) {} - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.fabView, mBinding.viewOptionsCard)); - mBinding.viewOptionsCard.setVisibility(View.VISIBLE); - mBinding.transformScrim.setVisibility(View.VISIBLE); - mBinding.fabView.setVisibility(View.GONE); - } - - private void hideViewOptions() { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewOptionsCard, mBinding.fabView)); - mBinding.viewOptionsCard.setVisibility(View.GONE); - mBinding.transformScrim.setVisibility(View.GONE); - mBinding.fabView.setVisibility(View.VISIBLE); - } - - private void showBuildResult() { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewBuildOut, mBinding.buildContainer)); - mBinding.buildContainer.setVisibility(View.VISIBLE); - mBinding.viewOptionsCard.setVisibility(View.GONE); - hideToolbarAndTabs(); - } - - private void hideBuildResult() { - showToolbarAndTabs(); - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.buildContainer, mBinding.viewBuildOut)); - mBinding.buildContainer.setVisibility(View.GONE); - mBinding.viewOptionsCard.setVisibility(View.VISIBLE); - } - - private void showLogResult() { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewLogs, mBinding.logContainer)); - mBinding.logContainer.setVisibility(View.VISIBLE); - mBinding.viewOptionsCard.setVisibility(View.GONE); - hideToolbarAndTabs(); - } - - private void hideLogResult() { - showToolbarAndTabs(); - - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.logContainer, mBinding.viewLogs)); - mBinding.logContainer.setVisibility(View.GONE); - mBinding.viewOptionsCard.setVisibility(View.VISIBLE); - } - - private void showDiagnostics() { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewDiags, mBinding.diagContainer)); - mBinding.diagContainer.setVisibility(View.VISIBLE); - mBinding.viewOptionsCard.setVisibility(View.GONE); - hideToolbarAndTabs(); - } - - private void hideDiagnostics() { - showToolbarAndTabs(); - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.diagContainer, mBinding.viewDiags)); - mBinding.diagContainer.setVisibility(View.GONE); - mBinding.viewOptionsCard.setVisibility(View.VISIBLE); - } - - private void showSearchResults() { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.viewSearchResults, mBinding.searchResultsContainer)); - mBinding.searchResultsContainer.setVisibility(View.VISIBLE); - mBinding.viewOptionsCard.setVisibility(View.GONE); - hideToolbarAndTabs(); - } - - private void hideSearchResults() { - showToolbarAndTabs(); - TransitionManager.beginDelayedTransition(mBinding.getRoot(), createContainerTransformFor(mBinding.searchResultsContainer, mBinding.viewSearchResults)); - mBinding.searchResultsContainer.setVisibility(View.GONE); - mBinding.viewOptionsCard.setVisibility(View.VISIBLE); - } - - public void handleDiagnosticsResultVisibility(boolean errorVisible) { - mBinding.diagEmptyView.setVisibility(errorVisible ? View.VISIBLE : View.GONE); - getDiagnosticsList().setVisibility(errorVisible ? View.GONE : View.VISIBLE); - } - - public void handleSearchResultVisibility(boolean errorVisible) { - mBinding.searchEmptyView.setVisibility(errorVisible ? View.VISIBLE : View.GONE); - getSearchResultList().setVisibility(errorVisible ? View.GONE : View.VISIBLE); - } - - private MaterialContainerTransform createContainerTransformFor(View start, View end) { - return createContainerTransformFor(start, end, mBinding.realContainer); - } - - private MaterialContainerTransform createContainerTransformFor(View start, View end, View drawingView) { - return TransformUtils.createContainerTransformFor(start, end, drawingView); - } - - private void install(File apk) { - if(apk.exists()) { - Intent i = IntentUtils.getInstallAppIntent(apk); - if(i != null) { - startActivity(i); - } - } - } - - private void createServices() { - new TaskExecutor().executeAsync(() -> { - getApp().createCompletionService(); - mJLSHandler.start(EditorActivity.this); - return null; - }, __ -> { - setStatus(getString(getApp().areCompletorsStarted() ? R.string.msg_service_started : R.string.msg_starting_completion_failed)); - }); - } private void registerLogReceiver() { IntentFilter filter = new IntentFilter(); filter.addAction(LogReceiver.APPEND_LOG); registerReceiver(mLogReceiver, filter); } - - private void setStatus(final CharSequence text) { - try { - runOnUiThread(() -> mBinding.editorStatusText.setText(text)); - } catch (Throwable th) { - } - } - - private void showFirstBuildNotice() { - new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog) - .setPositiveButton(android.R.string.ok, null) - .setTitle(R.string.title_first_build) - .setMessage(R.string.msg_first_build) - .setCancelable(false) - .create().show(); - } - @Override - public void onGroupClick(DiagnosticGroup group) { - if(group != null - && group.file != null - && group.file.exists() - && FileUtils.isUtf8(group.file)) - { - openFile(group.file); - } - } - - @Override - public void onDiagnosticClick(File file, Diagnostic diagnostic) { - openFileAndSelect(file, diagnostic.range); - hideDiagnostics(); + private IDEService getBuildService() { + return mBuildServiceHandler.getService(); } - public EditorPagerAdapter getPagerAdapter(){ - return mPagerAdapter; - } - - public ActivityEditorBinding getBinding() { - return mBinding; - } - - /** - * Positions the view to the provided coordinates and within screen - * - * @param view View to position - * @param initialX Initial X coordinate of view - * @param initialY Initial Y coordinate of view - */ - public void positionViewWithinScreen(View view, float initialX, float initialY) { - view.setX(initialX); - view.setY(initialY); - - Rect r = new Rect(); - int width = view.getWidth(); - initialX = view.getX(); - view.getWindowVisibleDisplayFrame(r); - if (r.width() != width) { - - /** - * Will be true when the view is going out of screen to left - */ - if (initialX < r.left) { - initialX = SizeUtils.dp2px(8); // an offset of 8dp from the left edge of screen - view.setX(initialX); - } - - /** - * Will be true when the view is going out of screen to right - */ - if (initialX + width > r.right) { - initialX = r.right - SizeUtils.dp2px(8) - width; // position to the right but leaving 8dp space from the right edge of screen - view.setX(initialX); - } - } - } - - public void openFileAndSelect(File file, Range range) { - openFile(file, range); - EditorFragment opened = mPagerAdapter.findEditorByFile(file); - if (opened != null && opened.getEditor() != null) { - CodeEditor editor = opened.getEditor(); - editor.post(() -> { - if (range.start.equals(range.end)) { - editor.setSelection(range.start.line, range.start.column); - } else { - editor.setSelectionRegion(range.start.line, range.start.column, range.end.line, range.end.column); - } - }); - } - } - - public void addPendingMessage(Message msg) { - pendingMessages.push(msg); - } - - public Message createMessage(String method, String data) { - Message msg = new Message(); - msg.jsonrpc = "2.0"; - msg.method = method; - msg.params = new JsonParser().parse(data); - return msg; - } - - /***************************** - **** Tab Related ************ - *****************************/ - - @Override - public void onTabSelected(TabLayout.Tab p1) { - EditorFragment current = mPagerAdapter.getFrag(p1.getPosition()); - if(current != null && current.getFile() != null) { - this.mCurrentFragment = current; - this.mCurrentFile = current.getFile(); - refreshSymbolInput(current); - } - - invalidateOptionsMenu(); - } - - @Override - public void onTabUnselected(TabLayout.Tab p1) { - EditorFragment frag = mPagerAdapter.getFrag(p1.getPosition()); - if(frag == null) return; - boolean isGradle = frag.isModified() && frag.getFile().getName().endsWith(EditorFragment.EXT_GRADLE); - frag.save(); - if(isGradle) { - notifySyncNeeded(); - } - } - - @Override - public void onTabReselected(TabLayout.Tab p1) { - mTabCloseAction.show(mBinding.tabs); - } - - // ************************ - // **** Navigation View - // ************************ - - @Override - public boolean onNavigationItemSelected(MenuItem p1) { - final int id = p1.getItemId(); - if(id == R.id.editornav_discuss) { - getApp().openTelegramGroup(); - } else if(id == R.id.editornav_suggest) { - getApp().openIssueTracker(); - } else if(id == R.id.editornav_needHelp) { - showNeedHelpDialog(); - } else if(id == R.id.editornav_settings) { - 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; - } - private void showNeedHelpDialog() { MaterialAlertDialogBuilder builder = new MaterialAlertDialogBuilder(this, R.style.AppTheme_MaterialAlertDialog); builder.setTitle(R.string.need_help); @@ -1079,212 +1168,6 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F builder.create().show(); } - // ----------------------------- - // ---- Fragment Related - // ------------------------------- - - - public void loadFragment(Fragment fragment) { - super.loadFragment(fragment, mBinding.editorFrameLayout.getId()); - } - - public ProjectInfoSheet getProjectInfoSheet() { - return mProjectInfoSheet == null ? createProjectInfoSheet() : mProjectInfoSheet; - } - - public ProjectInfoSheet createProjectInfoSheet() { - mProjectInfoSheet = new ProjectInfoSheet(); - mProjectInfoSheet.setProject(mIDEProject); - return mProjectInfoSheet; - } - - public OptionsListFragment getFileOptionsFragment(File file) { - mFileOptionsFragment = new OptionsListFragment(); - mFileOptionsFragment.addOption(new SheetOption(0, R.drawable.ic_file_copy_path, R.string.copy_path, file)); - mFileOptionsFragment.addOption(new SheetOption(1, R.drawable.ic_file_rename, R.string.rename_file, file)); - mFileOptionsFragment.addOption(new SheetOption(2, R.drawable.ic_file_delete, R.string.delete_file, file)); - if(file.isDirectory()) { - mFileOptionsFragment.addOption(new SheetOption(3, R.drawable.ic_new_file, R.string.new_file, file)); - mFileOptionsFragment.addOption(new SheetOption(4, R.drawable.ic_new_folder, R.string.new_folder, file)); - } - mFileOptionsFragment.setOnOptionsClickListener(o -> handleOptionClick(o)); - return mFileOptionsFragment; - } - - public ProgressSheet getProgressSheet() { - return mSearchingProgress; - } - - public ProgressSheet getProgressSheet(int msg) { - if(mSearchingProgress != null && mSearchingProgress.isShowing()) { - mSearchingProgress.dismiss(); - } - mSearchingProgress = new ProgressSheet(); - mSearchingProgress.setCancelable(false); - mSearchingProgress.setWelcomeTextEnabled(false); - mSearchingProgress.setMessage(getString(msg)); - mSearchingProgress.setSubMessageEnabled(false); - return mSearchingProgress; - } - - public MaterialBanner getSyncBanner() { - return mBinding.syncBanner.setContentTextColor(ContextCompat.getColor(this, R.color.primaryTextColor)) - .setBannerBackgroundColor(ContextCompat.getColor(this, R.color.primaryLightColor)) - .setButtonTextColor(ContextCompat.getColor(this, R.color.secondaryColor)) - .setIcon(R.drawable.ic_sync) - .setContentText(R.string.msg_sync_needed); - } - - public TextSheetFragment getDaemonStatusFragment() { - return mDaemonStatusFragment == null ? mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true).setTitleText(R.string.gradle_daemon_status) : mDaemonStatusFragment; - } - - public CodeEditor getBuildView() { - return getBuildView(false); - } - - public CodeEditor getBuildView(boolean setup) { - CodeEditor edit = buildView == null ? createBuildView() : buildView; - if(setup && edit.getParent() == null) { - setupContainers(); - } - return edit; - } - - public CodeEditor createBuildView() { - buildView = new CodeEditor(this); - buildView.setEditable(false); - buildView.setDividerWidth(0); - buildView.setEditorLanguage(new BuildOutputLanguage()); - buildView.setOverScrollEnabled(false); - buildView.setTextActionMode(CodeEditor.TextActionMode.ACTION_MODE); - buildView.setWordwrap(false); - buildView.setUndoEnabled(false); - buildView.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono()); - buildView.setTypefaceText(TypefaceUtils.jetbrainsMono()); - buildView.setTextSize(12); - buildView.setColorScheme(new SchemeAndroidIDE()); - return buildView; - } - - public CodeEditor getLogView() { - return getLogView(false); - } - - public CodeEditor getLogView(boolean setup) { - CodeEditor edit = logView == null ? createLogView() : logView; - if(setup && edit.getParent() == null) { - setupContainers(); - } - return edit; - } - - public CodeEditor createLogView() { - logView = new CodeEditor(this); - logView.setEditable(false); - logView.setDividerWidth(0); - logView.setEditorLanguage(getLogLanguage()); - logView.setOverScrollEnabled(false); - logView.setTextActionMode(CodeEditor.TextActionMode.ACTION_MODE); - logView.setWordwrap(false); - logView.setUndoEnabled(false); - logView.setTypefaceLineNumber(TypefaceUtils.jetbrainsMono()); - logView.setTypefaceText(TypefaceUtils.jetbrainsMono()); - logView.setTextSize(12); - buildView.setColorScheme(new SchemeAndroidIDE()); - return logView; - } - - public RecyclerView getDiagnosticsList() { - return diagnosticList == null ? createDiagnosticsList() : diagnosticList; - } - - public RecyclerView createDiagnosticsList() { - diagnosticList = new RecyclerView(this); - diagnosticList.setLayoutManager(new LinearLayoutManager(this)); - diagnosticList.setAdapter(mLanguageClientHandler.newDiagnosticsAdapter()); - return diagnosticList; - } - - public RecyclerView getSearchResultList() { - return searchResultList == null ? createSearchResultList() : searchResultList; - } - - public RecyclerView createSearchResultList() { - searchResultList = new RecyclerView(this); - searchResultList.setLayoutManager(new LinearLayoutManager(this)); - return searchResultList; - } - - public LogLanguageImpl getLogLanguage() { - return mLogLanguageImpl == null ? mLogLanguageImpl = new LogLanguageImpl() : mLogLanguageImpl; - } - - // ----------------------------- - // ---- File Related - // ----------------------------- - - @Override - public void openFile(File file) { - openFile(file, null); - } - - public EditorFragment openFile(File file, Range selection) { - if(selection == null) selection = Range.ofZero(); - int i = mPagerAdapter.openFile(file, selection, this, mJLSHandler); - if(i >= 0 && !mBinding.tabs.getTabAt(i).isSelected()) - mBinding.tabs.getTabAt(i).select(); - mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END); - invalidateOptionsMenu(); - try { - return mPagerAdapter.getFrag(i); - } catch (Throwable th) { - return null; - } - } - - @Override - public void onOpenSuccessful(File file, String text) { - if(file.getName().endsWith(".java")) { - TextDocumentItem doc = new TextDocumentItem(); - doc.languageId = "java"; - doc.uri = file.toURI(); - doc.text = text; - doc.version = VersionedFileManager.fileOpened(file); - DidOpenTextDocumentParams p = new DidOpenTextDocumentParams(); - p.textDocument = doc; - mJLSHandler.didOpen(p); - } - } - - @Override - public void showFileOptions(File thisFile, TreeNode node) { - mLastHolded = node; - getFileOptionsFragment(thisFile).show(getSupportFragmentManager(), TAG_FILE_OPTIONS_FRAGMENT); - } - - private boolean saveAll() { - return saveAll(true); - } - - private boolean saveAll(boolean notify) { - return saveAll(notify, false); - } - - private boolean saveAll(boolean notify, boolean canProcessResources) { - SaveResult result = mPagerAdapter.saveAll(); - if(notify) { - getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS); - } - if(result.gradleSaved){ - notifySyncNeeded(); - } - if(result.xmlSaved && canProcessResources && getBuildService() != null) { - getBuildService().updateResourceClasses(); - } - return result.gradleSaved; - } - private void handleOptionClick(SheetOption o) { if(o.extra != null && o.extra instanceof File) { final File f = (File) o.extra; @@ -1563,11 +1446,7 @@ public class EditorActivity extends StudioActivity implements FileTreeFragment.F }); builder.create().show(); } - - // ----------------------------- - // ---- Quick Actions - // ----------------------------- - + private void createQuickActions() { ActionItem closeThis = new ActionItem(ACTIONID_CLOSE, getString(R.string.action_closeThis), R.drawable.ic_close_this); ActionItem closeOthers = new ActionItem(ACTIONID_OTHERS, getString(R.string.action_closeOthers), R.drawable.ic_close_others); 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 468200af7..912fe64a4 100644 --- a/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java +++ b/app/src/main/java/com/itsaky/androidide/app/StudioActivity.java @@ -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(); 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 44dd4954d..f813097bb 100644 --- a/app/src/main/java/com/itsaky/androidide/app/StudioApp.java +++ b/app/src/main/java/com/itsaky/androidide/app/StudioApp.java @@ -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(); } diff --git a/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java new file mode 100644 index 000000000..773c4e4c7 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/handlers/BuildServiceHandler.java @@ -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 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 paths = new ArrayList(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"); + } +} diff --git a/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java index 5f44d84ea..900e5f101 100644 --- a/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java +++ b/app/src/main/java/com/itsaky/androidide/handlers/IDEHandler.java @@ -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!"; - } - } } diff --git a/app/src/main/java/com/itsaky/androidide/handlers/jls/JLSHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/jls/JLSHandler.java index bc2cd60fd..49089f669 100644 --- a/app/src/main/java/com/itsaky/androidide/handlers/jls/JLSHandler.java +++ b/app/src/main/java/com/itsaky/androidide/handlers/jls/JLSHandler.java @@ -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 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) { diff --git a/app/src/main/java/com/itsaky/androidide/handlers/jls/LanguageClientHandler.java b/app/src/main/java/com/itsaky/androidide/handlers/jls/LanguageClientHandler.java index 8b6fb49d1..61ddc8687 100644 --- a/app/src/main/java/com/itsaky/androidide/handlers/jls/LanguageClientHandler.java +++ b/app/src/main/java/com/itsaky/androidide/handlers/jls/LanguageClientHandler.java @@ -39,14 +39,15 @@ import java.util.Map; public class LanguageClientHandler extends IDEHandler implements LanguageClient { - private EditorActivity activity; private final Map> 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 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 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()); } /**