diff --git a/.idea/google-java-format.xml b/.idea/google-java-format.xml index 05946a82a..8b57f4527 100644 --- a/.idea/google-java-format.xml +++ b/.idea/google-java-format.xml @@ -2,6 +2,5 @@ \ No newline at end of file diff --git a/app/src/main/assets/data/common/tooling-api-all.jar b/app/src/main/assets/data/common/tooling-api-all.jar index 242297626..a5502f9a5 100644 Binary files a/app/src/main/assets/data/common/tooling-api-all.jar and b/app/src/main/assets/data/common/tooling-api-all.jar differ diff --git a/app/src/main/java/com/itsaky/androidide/EditorActivity.java b/app/src/main/java/com/itsaky/androidide/EditorActivity.java index 4ef7d10b0..4ca6e747e 100755 --- a/app/src/main/java/com/itsaky/androidide/EditorActivity.java +++ b/app/src/main/java/com/itsaky/androidide/EditorActivity.java @@ -79,6 +79,7 @@ import com.itsaky.androidide.adapters.DiagnosticsAdapter; import com.itsaky.androidide.adapters.EditorBottomSheetTabAdapter; import com.itsaky.androidide.adapters.SearchListAdapter; import com.itsaky.androidide.app.StudioActivity; +import com.itsaky.androidide.app.StudioApp; import com.itsaky.androidide.databinding.ActivityEditorBinding; import com.itsaky.androidide.databinding.LayoutDiagnosticInfoBinding; import com.itsaky.androidide.databinding.LayoutSearchProjectBinding; @@ -109,6 +110,7 @@ import com.itsaky.androidide.projects.ProjectManager; import com.itsaky.androidide.services.GradleBuildService; import com.itsaky.androidide.services.LogReceiver; import com.itsaky.androidide.shell.ShellServer; +import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData; import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult; import com.itsaky.androidide.tooling.api.model.IdeGradleProject; import com.itsaky.androidide.utils.CharSequenceInputStream; @@ -147,6 +149,7 @@ import java.util.Map; import java.util.Objects; import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletionException; +import java.util.concurrent.ExecutionException; import java.util.function.Consumer; import java.util.regex.Pattern; import java.util.stream.Collectors; @@ -157,1670 +160,1649 @@ import me.piruin.quickaction.ActionItem; import me.piruin.quickaction.QuickAction; public class EditorActivity extends StudioActivity - implements FileTreeFragment.FileActionListener, - TabLayout.OnTabSelectedListener, - NavigationView.OnNavigationItemSelectedListener, - DiagnosticClickListener, - IDEHandler.Provider, - EditorActivityProvider, - OptionsListFragment.OnOptionsClickListener { + implements FileTreeFragment.FileActionListener, + TabLayout.OnTabSelectedListener, + NavigationView.OnNavigationItemSelectedListener, + DiagnosticClickListener, + IDEHandler.Provider, + EditorActivityProvider, + OptionsListFragment.OnOptionsClickListener { - public static final String EXTRA_PROJECT_PATH = "project_path"; - public static final String KEY_BOTTOM_SHEET_SHOWN = "editor_bottomSheetShown"; - private static final String TAG_FILE_OPTIONS_FRAGMENT = "file_options_fragment"; - private static final int ACTION_ID_CLOSE = 100; - private static final int ACTION_ID_OTHERS = 101; - private static final int ACTION_ID_ALL = 102; - private static final ILogger LOG = ILogger.newInstance("EditorActivity"); - private final EditorEventListener mBuildEventListener = new EditorEventListener(); - private ActivityEditorBinding mBinding; - private LayoutDiagnosticInfoBinding mDiagnosticInfoBinding; - private EditorBottomSheetTabAdapter bottomSheetTabAdapter; - private final LogReceiver mLogReceiver = new LogReceiver().setLogListener(this::appendApkLog); - private FileTreeFragment mFileTreeFragment; - private TreeNode mLastHeld; - private SymbolInputView symbolInput; - private FileOptionsHandler mFileOptionsHandler; - private QuickAction mTabCloseAction; - private TextSheetFragment mDaemonStatusFragment; - private OptionsListFragment mFileOptionsFragment; - private ProgressSheet mSearchingProgress; - private AlertDialog mFindInProjectDialog; - private ActivityResultLauncher mUIDesignerLauncher; - private EditorBottomSheetBehavior mEditorBottomSheet; - private EditorViewModel mViewModel; + public static final String EXTRA_PROJECT_PATH = "project_path"; + public static final String KEY_BOTTOM_SHEET_SHOWN = "editor_bottomSheetShown"; + private static final String TAG_FILE_OPTIONS_FRAGMENT = "file_options_fragment"; + private static final int ACTION_ID_CLOSE = 100; + private static final int ACTION_ID_OTHERS = 101; + private static final int ACTION_ID_ALL = 102; + private static final ILogger LOG = ILogger.newInstance("EditorActivity"); + private final EditorEventListener mBuildEventListener = new EditorEventListener(); + private ActivityEditorBinding mBinding; + private LayoutDiagnosticInfoBinding mDiagnosticInfoBinding; + private EditorBottomSheetTabAdapter bottomSheetTabAdapter; + private final LogReceiver mLogReceiver = new LogReceiver().setLogListener(this::appendApkLog); + private FileTreeFragment mFileTreeFragment; + private TreeNode mLastHeld; + private SymbolInputView symbolInput; + private FileOptionsHandler mFileOptionsHandler; + private QuickAction mTabCloseAction; + private TextSheetFragment mDaemonStatusFragment; + private OptionsListFragment mFileOptionsFragment; + private ProgressSheet mSearchingProgress; + private AlertDialog mFindInProjectDialog; + private ActivityResultLauncher mUIDesignerLauncher; + private EditorBottomSheetBehavior mEditorBottomSheet; + private EditorViewModel mViewModel; - private IdeGradleProject mRootProject; - private GradleBuildService mBuildService; - private final ServiceConnection mGradleServiceConnection = - new ServiceConnection() { - @Override - public void onServiceConnected(ComponentName name, IBinder service) { - mBuildService = ((GradleBuildService.GradleServiceBinder) service).getService(); - LOG.info("Gradle build service has been started..."); + private GradleBuildService mBuildService; + private final ServiceConnection mGradleServiceConnection = + new ServiceConnection() { + @Override + public void onServiceConnected(ComponentName name, IBinder service) { + mBuildService = ((GradleBuildService.GradleServiceBinder) service).getService(); + LOG.info("Gradle build service has been started..."); - mBuildService - .setEventListener(mBuildEventListener) - .startToolingServer(() -> initializeProject()); - } - - @Override - public void onServiceDisconnected(ComponentName name) { - mBuildService = null; - LOG.info("Disconnected from Gradle build service..."); - } - }; - - @Override - public void onBackPressed() { - if (mBinding.getRoot().isDrawerOpen(GravityCompat.END)) { - mBinding.getRoot().closeDrawer(GravityCompat.END); - } else if (mBinding.getRoot().isDrawerOpen(GravityCompat.START)) { - mBinding.getRoot().closeDrawer(GravityCompat.START); - } else if (getDaemonStatusFragment().isShowing()) { - getDaemonStatusFragment().dismiss(); - } else if (mFileOptionsFragment != null && mFileOptionsFragment.isShowing()) { - mFileOptionsFragment.dismiss(); - } else if (mEditorBottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) { - mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); - } else { - confirmProjectClose(); - } - } - - @SuppressLint("RestrictedApi") - @Override - public boolean onCreateOptionsMenu(Menu menu) { - if (menu instanceof MenuBuilder) { - MenuBuilder builder = (MenuBuilder) menu; - builder.setOptionalIconsVisible(true); + mBuildService + .setEventListener(mBuildEventListener) + .startToolingServer(() -> initializeProject()); } - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - ensureToolbarMenu(menu); - return true; - } - - public void ensureToolbarMenu(@NonNull Menu menu) { - menu.clear(); - - final var data = new ActionData(); - final var currentEditor = getCurrentEditor(); - data.put(Context.class, EditorActivity.this); - data.put(CodeEditorView.class, currentEditor); - - if (currentEditor != null) { - data.put(IDEEditor.class, currentEditor.getEditor()); - data.put(File.class, currentEditor.getEditor().getFile()); + @Override + public void onServiceDisconnected(ComponentName name) { + mBuildService = null; + LOG.info("Disconnected from Gradle build service..."); } + }; - ActionsRegistry.getInstance() - .fillMenu( - data, - com.itsaky.androidide.actions.ActionItem.Location.EDITOR_TOOLBAR, - menu); + @Override + public void onBackPressed() { + if (mBinding.getRoot().isDrawerOpen(GravityCompat.END)) { + mBinding.getRoot().closeDrawer(GravityCompat.END); + } else if (mBinding.getRoot().isDrawerOpen(GravityCompat.START)) { + mBinding.getRoot().closeDrawer(GravityCompat.START); + } else if (getDaemonStatusFragment().isShowing()) { + getDaemonStatusFragment().dismiss(); + } else if (mFileOptionsFragment != null && mFileOptionsFragment.isShowing()) { + mFileOptionsFragment.dismiss(); + } else if (mEditorBottomSheet.getState() == BottomSheetBehavior.STATE_EXPANDED) { + mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); + } else { + confirmProjectClose(); + } + } + + @SuppressLint("RestrictedApi") + @Override + public boolean onCreateOptionsMenu(Menu menu) { + if (menu instanceof MenuBuilder) { + MenuBuilder builder = (MenuBuilder) menu; + builder.setOptionalIconsVisible(true); } - @Nullable - public CodeEditorView getCurrentEditor() { - if (mViewModel.getCurrentFileIndex() != -1) { - return getEditorAtIndex(mViewModel.getCurrentFileIndex()); - } + return true; + } - return null; + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + ensureToolbarMenu(menu); + return true; + } + + public void ensureToolbarMenu(@NonNull Menu menu) { + menu.clear(); + + final var data = new ActionData(); + final var currentEditor = getCurrentEditor(); + data.put(Context.class, EditorActivity.this); + data.put(CodeEditorView.class, currentEditor); + + if (currentEditor != null) { + data.put(IDEEditor.class, currentEditor.getEditor()); + data.put(File.class, currentEditor.getEditor().getFile()); } - @Nullable - public CodeEditorView getEditorAtIndex(final int index) { - return (CodeEditorView) mBinding.editorContainer.getChildAt(index); + ActionsRegistry.getInstance() + .fillMenu(data, com.itsaky.androidide.actions.ActionItem.Location.EDITOR_TOOLBAR, menu); + } + + @Nullable + public CodeEditorView getCurrentEditor() { + if (mViewModel.getCurrentFileIndex() != -1) { + return getEditorAtIndex(mViewModel.getCurrentFileIndex()); } - @Override - public EditorActivity provide() { - return this; + return null; + } + + @Nullable + public CodeEditorView getEditorAtIndex(final int index) { + return (CodeEditorView) mBinding.editorContainer.getChildAt(index); + } + + @Override + public EditorActivity provide() { + return this; + } + + @Override + public EditorActivity provideEditorActivity() { + return this; + } + + public void handleSearchResults(Map> results) { + setSearchResultAdapter( + new com.itsaky.androidide.adapters.SearchListAdapter( + results, + file -> { + openFile(file); + hideViewOptions(); + }, + match -> { + openFileAndSelect(match.file, match); + hideViewOptions(); + })); + + showSearchResults(); + + if (mSearchingProgress != null && mSearchingProgress.isShowing()) { + mSearchingProgress.dismiss(); + } + } + + public void appendApkLog(LogLine line) { + final var logFragment = bottomSheetTabAdapter.getLogFragment(); + if (logFragment != null) { + logFragment.appendLog(line); + } + } + + public void showDaemonStatus() { + ShellServer shell = getApp().newShell(t -> getDaemonStatusFragment().append(t)); + shell.bgAppend(String.format("echo '%s'", getString(R.string.msg_getting_daemom_status))); + shell.bgAppend( + String.format( + "cd '%s' && sh gradlew --status", + Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath()))); + if (!getDaemonStatusFragment().isShowing()) { + getDaemonStatusFragment().show(getSupportFragmentManager(), "daemon_status"); + } + } + + public TextSheetFragment getDaemonStatusFragment() { + return mDaemonStatusFragment == null + ? mDaemonStatusFragment = + new TextSheetFragment() + .setTextSelectable(true) + .setTitleText(R.string.gradle_daemon_status) + : mDaemonStatusFragment; + } + + public void showSearchResults() { + if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) { + mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); } - @Override - public EditorActivity provideEditorActivity() { - return this; + final int index = bottomSheetTabAdapter.findIndexOfFragmentByClass(SearchResultFragment.class); + if (index >= 0 && index < mBinding.bottomSheet.tabs.getTabCount()) { + final var tab = mBinding.bottomSheet.tabs.getTabAt(index); + if (tab != null) { + tab.select(); + } } + } - public void handleSearchResults(Map> results) { - setSearchResultAdapter( - new com.itsaky.androidide.adapters.SearchListAdapter( - results, - file -> { - openFile(file); - hideViewOptions(); - }, - match -> { - openFileAndSelect(match.file, match); - hideViewOptions(); - })); + public void handleDiagnosticsResultVisibility(boolean errorVisible) { + runOnUiThread( + () -> { + final var diagnosticsFragment = bottomSheetTabAdapter.getDiagnosticsFragment(); + if (diagnosticsFragment != null) { + diagnosticsFragment.handleResultVisibility(errorVisible); + } + }); + } - showSearchResults(); + public void handleSearchResultVisibility(boolean errorVisible) { + runOnUiThread( + () -> { + final var searchResultFragment = bottomSheetTabAdapter.getSearchResultFragment(); + if (searchResultFragment != null) { + searchResultFragment.handleResultVisibility(errorVisible); + } + }); + } - if (mSearchingProgress != null && mSearchingProgress.isShowing()) { - mSearchingProgress.dismiss(); - } + public void showFirstBuildNotice() { + DialogUtils.newMaterialDialogBuilder(this) + .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); + hideViewOptions(); } + } - public void appendApkLog(LogLine line) { - final var logFragment = bottomSheetTabAdapter.getLogFragment(); - if (logFragment != null) { - logFragment.appendLog(line); - } - } + @Override + public void onDiagnosticClick(File file, @NonNull DiagnosticItem diagnostic) { + openFileAndSelect(file, diagnostic.getRange()); + hideViewOptions(); + } - public void showDaemonStatus() { - ShellServer shell = getApp().newShell(t -> getDaemonStatusFragment().append(t)); - shell.bgAppend(String.format("echo '%s'", getString(R.string.msg_getting_daemom_status))); - shell.bgAppend( - String.format( - "cd '%s' && sh gradlew --status", - Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath()))); - if (!getDaemonStatusFragment().isShowing()) { - getDaemonStatusFragment().show(getSupportFragmentManager(), "daemon_status"); - } - } - - public TextSheetFragment getDaemonStatusFragment() { - return mDaemonStatusFragment == null - ? mDaemonStatusFragment = - new TextSheetFragment() - .setTextSelectable(true) - .setTitleText(R.string.gradle_daemon_status) - : mDaemonStatusFragment; - } - - public void showSearchResults() { - if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) { - mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); - } - - final int index = - bottomSheetTabAdapter.findIndexOfFragmentByClass(SearchResultFragment.class); - if (index >= 0 && index < mBinding.bottomSheet.tabs.getTabCount()) { - final var tab = mBinding.bottomSheet.tabs.getTabAt(index); - if (tab != null) { - tab.select(); - } - } - } - - public void handleDiagnosticsResultVisibility(boolean errorVisible) { - runOnUiThread( - () -> { - final var diagnosticsFragment = bottomSheetTabAdapter.getDiagnosticsFragment(); - if (diagnosticsFragment != null) { - diagnosticsFragment.handleResultVisibility(errorVisible); - } - }); - } - - public void handleSearchResultVisibility(boolean errorVisible) { - runOnUiThread( - () -> { - final var searchResultFragment = - bottomSheetTabAdapter.getSearchResultFragment(); - if (searchResultFragment != null) { - searchResultFragment.handleResultVisibility(errorVisible); - } - }); - } - - public void showFirstBuildNotice() { - DialogUtils.newMaterialDialogBuilder(this) - .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); - hideViewOptions(); - } - } - - @Override - public void onDiagnosticClick(File file, @NonNull DiagnosticItem diagnostic) { - openFileAndSelect(file, diagnostic.getRange()); - hideViewOptions(); - } - - public void openFileAndSelect(File file, Range selection) { - openFile(file, selection); - final var opened = getEditorForFile(file); - if (opened != null && opened.getEditor() != null) { - final var editor = opened.getEditor(); - final var range = editor.validateRange(selection); - editor.post( - () -> { - if (LSPUtils.isEqual(range.getStart(), range.getEnd())) { - editor.setSelection( - range.getStart().getLine(), range.getEnd().getColumn()); - } else { - editor.setSelection(range); - } - }); - } - } - - @Nullable - public CodeEditorView getEditorForFile(@NonNull final File file) { - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - final CodeEditorView editor = (CodeEditorView) mBinding.editorContainer.getChildAt(i); - if (file.equals(editor.getFile())) { - return editor; - } - } - - return null; - } - - @Override - public CodeEditorView openFile(File file) { - return openFile(file, null); - } - - public void hideViewOptions() { - if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_COLLAPSED) { - mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); - } - } - - public CodeEditorView openFile(File file, com.itsaky.lsp.models.Range selection) { - if (selection == null) { - selection = com.itsaky.lsp.models.Range.NONE; - } - - int index = openFileAndGetIndex(file, selection); - final var tab = mBinding.tabs.getTabAt(index); - if (tab != null && index >= 0 && !tab.isSelected()) { - tab.select(); - } - - if (mBinding.editorDrawerLayout.isDrawerOpen(GravityCompat.END)) { - mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END); - } - - mBinding.editorContainer.setDisplayedChild(index); - - try { - return getEditorAtIndex(index); - } catch (Throwable th) { - LOG.error("Unable to get editor fragment at opened file index", index); - LOG.error(th); - return null; - } - } - - @SuppressLint("NotifyDataSetChanged") - public int openFileAndGetIndex(File file, com.itsaky.lsp.models.Range selection) { - final var openedFileIndex = findIndexOfEditorByFile(file); - - if (openedFileIndex != -1) { - LOG.error("File is already opened. File: " + file); - return openedFileIndex; - } - - final var position = mViewModel.getOpenedFileCount(); - - LOG.info("Opening file at index:", position, "file: ", file); - - final var editor = new CodeEditorView(this, file, selection); - editor.getEditor().subscribeEvent(ContentChangeEvent.class, this::onEditorContentChanged); - editor.setLayoutParams( - new ViewGroup.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); - - mBinding.editorContainer.addView(editor); - mBinding.editorContainer.setDisplayedChild(position); - mBinding.tabs.addTab(mBinding.tabs.newTab().setText(file.getName())); - - mViewModel.addFile(file); - mViewModel.setCurrentFile(position, file); - - return position; - } - - public int findIndexOfEditorByFile(File file) { - if (file == null) { - LOG.error("Cannot find index of a null file."); - return -1; - } - - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - final var opened = mViewModel.getOpenedFile(i); - if (opened.equals(file)) { - return i; - } - } - - return -1; - } - - private void onEditorContentChanged(ContentChangeEvent event, Unsubscribe unsubscribe) { - if (event.getAction() != ContentChangeEvent.ACTION_SET_NEW_TEXT) { - mViewModel.setFilesModified(true); - } - } - - @Override - public void showFileOptions(File thisFile, TreeNode node) { - mLastHeld = node; - getFileOptionsFragment(thisFile) - .show(getSupportFragmentManager(), TAG_FILE_OPTIONS_FRAGMENT); - } - - 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_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)); - } - - return mFileOptionsFragment; - } - - public ActivityEditorBinding getBinding() { - return mBinding; - } - - public LayoutDiagnosticInfoBinding getDiagnosticBinding() { - return mDiagnosticInfoBinding; - } - - @Override - public void onTabSelected(@NonNull TabLayout.Tab tab) { - final var position = tab.getPosition(); - mBinding.editorContainer.setDisplayedChild(position); - - final var editorView = getEditorAtIndex(position); - Objects.requireNonNull(editorView); - - editorView.onEditorSelected(); - mViewModel.setCurrentFile(position, editorView.getFile()); - refreshSymbolInput(editorView); - } - - @Override - public void onTabUnselected(@NonNull TabLayout.Tab tab) { - final var position = tab.getPosition(); - final var editorView = getEditorAtIndex(position); - if (editorView != null) { - final var editor = editorView.getEditor(); - if (editor != null) { - editor.ensureWindowsDismissed(); - } - } - } - - @Override - public void onTabReselected(@NonNull TabLayout.Tab tab) { - mTabCloseAction.show(tab.view); - } - - private void refreshSymbolInput(@NonNull CodeEditorView frag) { - symbolInput.bindEditor(frag.getEditor()); - symbolInput.setSymbols(Symbols.forFile(frag.getFile())); - } - - @Override - public boolean onNavigationItemSelected(@NonNull MenuItem p1) { - final int id = p1.getItemId(); - if (id == R.id.editornav_discuss) { - getApp().openTelegramGroup(); - } else if (id == R.id.editornav_suggest) { - getApp().openGitHub(); - } 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(); - } else if (id == R.id.editornav_terminal) { - openTerminal(); - } - mBinding.getRoot().closeDrawer(GravityCompat.START); - return false; - } - - public void loadFragment(Fragment fragment) { - super.loadFragment(fragment, mBinding.editorFrameLayout.getId()); - } - - public FileTreeFragment getFileTreeFragment() { - return mFileTreeFragment; - } - - public TreeNode getLastHoldTreeNode() { - return mLastHeld; - } - - 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 void setDiagnosticsAdapter(@NonNull final DiagnosticsAdapter adapter) { - runOnUiThread( - () -> { - final var diagnosticsFragment = bottomSheetTabAdapter.getDiagnosticsFragment(); - if (diagnosticsFragment != null) { - diagnosticsFragment.setAdapter(adapter); - } - }); - } - - public void setSearchResultAdapter(@NonNull final SearchListAdapter adapter) { - runOnUiThread( - () -> { - final var searchResultFragment = - bottomSheetTabAdapter.getSearchResultFragment(); - if (searchResultFragment != null) { - searchResultFragment.setAdapter(adapter); - } - }); - } - - @SuppressWarnings("UnusedReturnValue") - public boolean saveAll() { - return saveAll(true); - } - - public void closeFile(int index) { - if (index >= 0 && index < mViewModel.getOpenedFileCount()) { - var opened = mViewModel.getOpenedFile(index); - LOG.info("Closing file:", opened); - final var editor = getEditorAtIndex(index); - - if (editor != null && editor.isModified()) { - notifyFilesUnsaved(Collections.singletonList(editor), () -> closeFile(index)); - return; - } - - if (editor != null && editor.getEditor() != null) { - editor.getEditor().close(); + public void openFileAndSelect(File file, Range selection) { + openFile(file, selection); + final var opened = getEditorForFile(file); + if (opened != null && opened.getEditor() != null) { + final var editor = opened.getEditor(); + final var range = editor.validateRange(selection); + editor.post( + () -> { + if (LSPUtils.isEqual(range.getStart(), range.getEnd())) { + editor.setSelection(range.getStart().getLine(), range.getEnd().getColumn()); } else { - LOG.error("Cannot save file before close. Editor instance is null"); + editor.setSelection(range); } + }); + } + } - mViewModel.removeFile(index); - mBinding.tabs.removeTabAt(index); - mBinding.editorContainer.removeViewAt(index); + @Nullable + public CodeEditorView getEditorForFile(@NonNull final File file) { + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + final CodeEditorView editor = (CodeEditorView) mBinding.editorContainer.getChildAt(i); + if (file.equals(editor.getFile())) { + return editor; + } + } + + return null; + } + + @Override + public CodeEditorView openFile(File file) { + return openFile(file, null); + } + + public void hideViewOptions() { + if (mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_COLLAPSED) { + mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); + } + } + + public CodeEditorView openFile(File file, com.itsaky.lsp.models.Range selection) { + if (selection == null) { + selection = com.itsaky.lsp.models.Range.NONE; + } + + int index = openFileAndGetIndex(file, selection); + final var tab = mBinding.tabs.getTabAt(index); + if (tab != null && index >= 0 && !tab.isSelected()) { + tab.select(); + } + + if (mBinding.editorDrawerLayout.isDrawerOpen(GravityCompat.END)) { + mBinding.editorDrawerLayout.closeDrawer(GravityCompat.END); + } + + mBinding.editorContainer.setDisplayedChild(index); + + try { + return getEditorAtIndex(index); + } catch (Throwable th) { + LOG.error("Unable to get editor fragment at opened file index", index); + LOG.error(th); + return null; + } + } + + @SuppressLint("NotifyDataSetChanged") + public int openFileAndGetIndex(File file, com.itsaky.lsp.models.Range selection) { + final var openedFileIndex = findIndexOfEditorByFile(file); + + if (openedFileIndex != -1) { + LOG.error("File is already opened. File: " + file); + return openedFileIndex; + } + + final var position = mViewModel.getOpenedFileCount(); + + LOG.info("Opening file at index:", position, "file: ", file); + + final var editor = new CodeEditorView(this, file, selection); + editor.getEditor().subscribeEvent(ContentChangeEvent.class, this::onEditorContentChanged); + editor.setLayoutParams( + new ViewGroup.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); + + mBinding.editorContainer.addView(editor); + mBinding.editorContainer.setDisplayedChild(position); + mBinding.tabs.addTab(mBinding.tabs.newTab().setText(file.getName())); + + mViewModel.addFile(file); + mViewModel.setCurrentFile(position, file); + + return position; + } + + public int findIndexOfEditorByFile(File file) { + if (file == null) { + LOG.error("Cannot find index of a null file."); + return -1; + } + + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + final var opened = mViewModel.getOpenedFile(i); + if (opened.equals(file)) { + return i; + } + } + + return -1; + } + + private void onEditorContentChanged(ContentChangeEvent event, Unsubscribe unsubscribe) { + if (event.getAction() != ContentChangeEvent.ACTION_SET_NEW_TEXT) { + mViewModel.setFilesModified(true); + } + } + + @Override + public void showFileOptions(File thisFile, TreeNode node) { + mLastHeld = node; + getFileOptionsFragment(thisFile).show(getSupportFragmentManager(), TAG_FILE_OPTIONS_FRAGMENT); + } + + 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_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)); + } + + return mFileOptionsFragment; + } + + public ActivityEditorBinding getBinding() { + return mBinding; + } + + public LayoutDiagnosticInfoBinding getDiagnosticBinding() { + return mDiagnosticInfoBinding; + } + + @Override + public void onTabSelected(@NonNull TabLayout.Tab tab) { + final var position = tab.getPosition(); + mBinding.editorContainer.setDisplayedChild(position); + + final var editorView = getEditorAtIndex(position); + Objects.requireNonNull(editorView); + + editorView.onEditorSelected(); + mViewModel.setCurrentFile(position, editorView.getFile()); + refreshSymbolInput(editorView); + } + + @Override + public void onTabUnselected(@NonNull TabLayout.Tab tab) { + final var position = tab.getPosition(); + final var editorView = getEditorAtIndex(position); + if (editorView != null) { + final var editor = editorView.getEditor(); + if (editor != null) { + editor.ensureWindowsDismissed(); + } + } + } + + @Override + public void onTabReselected(@NonNull TabLayout.Tab tab) { + mTabCloseAction.show(tab.view); + } + + private void refreshSymbolInput(@NonNull CodeEditorView frag) { + symbolInput.bindEditor(frag.getEditor()); + symbolInput.setSymbols(Symbols.forFile(frag.getFile())); + } + + @Override + public boolean onNavigationItemSelected(@NonNull MenuItem p1) { + final int id = p1.getItemId(); + if (id == R.id.editornav_discuss) { + getApp().openTelegramGroup(); + } else if (id == R.id.editornav_suggest) { + getApp().openGitHub(); + } 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(); + } else if (id == R.id.editornav_terminal) { + openTerminal(); + } + mBinding.getRoot().closeDrawer(GravityCompat.START); + return false; + } + + public void loadFragment(Fragment fragment) { + super.loadFragment(fragment, mBinding.editorFrameLayout.getId()); + } + + public FileTreeFragment getFileTreeFragment() { + return mFileTreeFragment; + } + + public TreeNode getLastHoldTreeNode() { + return mLastHeld; + } + + 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 void setDiagnosticsAdapter(@NonNull final DiagnosticsAdapter adapter) { + runOnUiThread( + () -> { + final var diagnosticsFragment = bottomSheetTabAdapter.getDiagnosticsFragment(); + if (diagnosticsFragment != null) { + diagnosticsFragment.setAdapter(adapter); + } + }); + } + + public void setSearchResultAdapter(@NonNull final SearchListAdapter adapter) { + runOnUiThread( + () -> { + final var searchResultFragment = bottomSheetTabAdapter.getSearchResultFragment(); + if (searchResultFragment != null) { + searchResultFragment.setAdapter(adapter); + } + }); + } + + @SuppressWarnings("UnusedReturnValue") + public boolean saveAll() { + return saveAll(true); + } + + public void closeFile(int index) { + if (index >= 0 && index < mViewModel.getOpenedFileCount()) { + var opened = mViewModel.getOpenedFile(index); + LOG.info("Closing file:", opened); + final var editor = getEditorAtIndex(index); + + if (editor != null && editor.isModified()) { + notifyFilesUnsaved(Collections.singletonList(editor), () -> closeFile(index)); + return; + } + + if (editor != null && editor.getEditor() != null) { + editor.getEditor().close(); + } else { + LOG.error("Cannot save file before close. Editor instance is null"); + } + + mViewModel.removeFile(index); + mBinding.tabs.removeTabAt(index); + mBinding.editorContainer.removeViewAt(index); + } else { + LOG.error("Invalid file index. Cannot close."); + return; + } + + mBinding.tabs.requestLayout(); + } + + public void closeAll() { + closeAll(null); + } + + /** + * Close all opened files. If all the files are saved successfully, then the provided {@link + * Runnable} will be called. + * + * @param onSaved The {@link Runnable} to run when all the files are saved. + */ + public void closeAll(Runnable onSaved) { + final var count = mViewModel.getOpenedFileCount(); + final var unsavedFiles = + mViewModel.getOpenedFiles().stream() + .map(this::getEditorForFile) + .filter(editor -> editor != null && editor.isModified()) + .collect(Collectors.toList()); + + if (unsavedFiles.isEmpty()) { + // Files were already saved, close all files one by one + for (int i = 0; i < count; i++) { + final var editor = getEditorAtIndex(i); + if (editor != null && editor.getEditor() != null) { + editor.getEditor().close(); } else { - LOG.error("Invalid file index. Cannot close."); - return; + LOG.error("Unable to close file at index:", i); } + } - mBinding.tabs.requestLayout(); + mViewModel.removeAllFiles(); + mBinding.tabs.removeAllTabs(); + mBinding.tabs.requestLayout(); + mBinding.editorContainer.removeAllViews(); + + if (onSaved != null) { + onSaved.run(); + } + } else { + // There are unsaved files + notifyFilesUnsaved(unsavedFiles, () -> closeAll(onSaved)); } + } - public void closeAll() { - closeAll(null); - } + public boolean areFilesModified() { + return mViewModel.areFilesModified(); + } - /** - * Close all opened files. If all the files are saved successfully, then the provided {@link - * Runnable} will be called. - * - * @param onSaved The {@link Runnable} to run when all the files are saved. - */ - public void closeAll(Runnable onSaved) { - final var count = mViewModel.getOpenedFileCount(); - final var unsavedFiles = - mViewModel.getOpenedFiles().stream() - .map(this::getEditorForFile) - .filter(editor -> editor != null && editor.isModified()) - .collect(Collectors.toList()); + public EditorViewModel getViewModel() { + return mViewModel; + } - if (unsavedFiles.isEmpty()) { - // Files were already saved, close all files one by one - for (int i = 0; i < count; i++) { - final var editor = getEditorAtIndex(i); - if (editor != null && editor.getEditor() != null) { - editor.getEditor().close(); - } else { - LOG.error("Unable to close file at index:", i); - } - } + public void closeOthers() { + final var unsavedFiles = + mViewModel.getOpenedFiles().stream() + .map(this::getEditorForFile) + .filter(editor -> editor != null && editor.isModified()) + .collect(Collectors.toList()); - mViewModel.removeAllFiles(); - mBinding.tabs.removeAllTabs(); - mBinding.tabs.requestLayout(); - mBinding.editorContainer.removeAllViews(); + if (unsavedFiles.isEmpty()) { + final var file = mViewModel.getCurrentFile(); + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + final var editor = getEditorAtIndex(i); - if (onSaved != null) { - onSaved.run(); - } + // Index of files changes as we keep close files + // So we compare the files instead of index + if (editor != null) { + if (!file.equals(editor.getFile())) { + closeFile(i); + } } else { - // There are unsaved files - notifyFilesUnsaved(unsavedFiles, () -> closeAll(onSaved)); + LOG.error("Unable to save file at index:", i); } + } + } else { + notifyFilesUnsaved(unsavedFiles, this::closeOthers); + } + } + + public void previewLayout() { + try { + + if (getCurrentEditor() == null || getCurrentEditor().getFile() == null) { + LOG.error("No file is opened. Cannot preview layout."); + return; + } + + saveAll(false); + + getResourceDirPaths() + .whenComplete( + (dirs, error) -> { + runOnUiThread( + () -> { + final Intent intent = new Intent(this, DesignerActivity.class); + intent.putExtra( + DesignerActivity.KEY_LAYOUT_PATH, + getCurrentEditor().getFile().getAbsolutePath()); + intent.putStringArrayListExtra(DesignerActivity.KEY_RES_DIRS, dirs); + LOG.info("Launching UI Designer..."); + mUIDesignerLauncher.launch(intent); + }); + }); + } catch (Throwable th) { + LOG.error(getString(R.string.err_cannot_preview_layout), th); + getApp().toast(R.string.msg_cannot_preview_layout, Toaster.Type.ERROR); + } + } + + public boolean saveAll(boolean notify) { + return saveAll(notify, false); + } + + @NonNull + private CompletableFuture> getResourceDirPaths() { + return getResourceDirs() + .thenApply( + files -> + files.stream() + .filter(Objects::nonNull) + .map(File::getAbsolutePath) + .collect(Collectors.toCollection(ArrayList::new))); + } + + public boolean saveAll(boolean notify, boolean canProcessResources) { + SaveResult result = saveAllResult(); + + if (notify) { + getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS); } - public boolean areFilesModified() { - return mViewModel.areFilesModified(); + if (result.gradleSaved) { + notifySyncNeeded(); } - public EditorViewModel getViewModel() { - return mViewModel; + if (canProcessResources) { + ProjectManager.INSTANCE.generateSources(mBuildService); } - public void closeOthers() { - final var unsavedFiles = - mViewModel.getOpenedFiles().stream() - .map(this::getEditorForFile) - .filter(editor -> editor != null && editor.isModified()) - .collect(Collectors.toList()); + return result.gradleSaved; + } - if (unsavedFiles.isEmpty()) { - final var file = mViewModel.getCurrentFile(); - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - final var editor = getEditorAtIndex(i); + private CompletableFuture> getResourceDirs() { + return ProjectManager.INSTANCE.getApplicationResDirectories(); + } - // Index of files changes as we keep close files - // So we compare the files instead of index - if (editor != null) { - if (!file.equals(editor.getFile())) { - closeFile(i); - } - } else { - LOG.error("Unable to save file at index:", i); - } - } - } else { - notifyFilesUnsaved(unsavedFiles, this::closeOthers); - } + public SaveResult saveAllResult() { + SaveResult result = new SaveResult(); + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + saveResult(i, result); } - public void previewLayout() { - try { + return result; + } - if (getCurrentEditor() == null || getCurrentEditor().getFile() == null) { - LOG.error("No file is opened. Cannot preview layout."); - return; - } + public void notifySyncNeeded() { + if (mBuildService != null && !mBuildService.isBuildInProgress()) { + getSyncBanner() + .setNegative(android.R.string.cancel, null) + .setPositive(android.R.string.ok, v -> initializeProject()) + .show(); + } + } - saveAll(false); + public void saveResult(int index, SaveResult result) { + if (index >= 0 && index < mViewModel.getOpenedFileCount()) { + var frag = getEditorAtIndex(index); + if (frag == null || frag.getFile() == null) { + return; + } - final Intent intent = new Intent(this, DesignerActivity.class); - intent.putExtra( - DesignerActivity.KEY_LAYOUT_PATH, - getCurrentEditor().getFile().getAbsolutePath()); - intent.putStringArrayListExtra( - DesignerActivity.KEY_RES_DIRS, (ArrayList) getResourceDirectories()); - LOG.info("Launching UI Designer..."); - mUIDesignerLauncher.launch(intent); - } catch (Throwable th) { - LOG.error(getString(R.string.err_cannot_preview_layout), th); - getApp().toast(R.string.msg_cannot_preview_layout, Toaster.Type.ERROR); - } + // Must be called before frag.save() + // Otherwise, it'll always return false + final boolean modified = frag.isModified(); + + frag.save(); + + final boolean isGradle = frag.getFile().getName().endsWith(".gradle"); + final boolean isXml = frag.getFile().getName().endsWith(".xml"); + + if (!result.gradleSaved) { + result.gradleSaved = modified && isGradle; + } + + if (!result.xmlSaved) { + result.xmlSaved = modified && isXml; + } } - public boolean saveAll(boolean notify) { - return saveAll(notify, false); + var modified = false; + for (var file : mViewModel.getOpenedFiles()) { + var editor = getEditorForFile(file); + if (editor == null) { + continue; + } + modified = modified || editor.isModified(); } - @NonNull - private List getResourceDirectories() { - final var dirs = ProjectManager.INSTANCE.getApplicationResDirectories(); - if (dirs == null) { - return Collections.emptyList(); - } + final boolean finalModified = modified; + ThreadUtils.runOnUiThread(() -> mViewModel.setFilesModified(finalModified)); + } - return dirs.stream().map(File::getAbsolutePath).collect(Collectors.toList()); + 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); + } + + private void initializeProject() { + final var projectPath = ProjectManager.INSTANCE.getProjectDirPath(); + if (projectPath == null) { + LOG.error("Cannot initialize project. Project model is null."); + return; } - public boolean saveAll(boolean notify, boolean canProcessResources) { - SaveResult result = saveAllResult(); - - if (notify) { - getApp().toast(R.string.all_saved, Toaster.Type.SUCCESS); - } - - if (result.gradleSaved) { - notifySyncNeeded(); - } - - if (canProcessResources) { - ProjectManager.INSTANCE.generateSources(mBuildService); - } - - return result.gradleSaved; + final var projectDir = new File(projectPath); + if (!projectDir.exists()) { + LOG.error("Project directory does not exist. Cannot initialize project"); + return; } - public SaveResult saveAllResult() { - SaveResult result = new SaveResult(); - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - saveResult(i, result); - } + ThreadUtils.runOnUiThread( + () -> { + setStatus(getString(R.string.msg_initializing_project)); + mBinding.buildProgressIndicator.setVisibility(View.VISIBLE); + }); - return result; - } - - public void notifySyncNeeded() { - if (mBuildService != null && !mBuildService.isBuildInProgress()) { - getSyncBanner() - .setNegative(android.R.string.cancel, null) - .setPositive(android.R.string.ok, v -> initializeProject()) - .show(); - } - } - - public void saveResult(int index, SaveResult result) { - if (index >= 0 && index < mViewModel.getOpenedFileCount()) { - var frag = getEditorAtIndex(index); - if (frag == null || frag.getFile() == null) { - return; - } - - // Must be called before frag.save() - // Otherwise, it'll always return false - final boolean modified = frag.isModified(); - - frag.save(); - - final boolean isGradle = frag.getFile().getName().endsWith(".gradle"); - final boolean isXml = frag.getFile().getName().endsWith(".xml"); - - if (!result.gradleSaved) { - result.gradleSaved = modified && isGradle; - } - - if (!result.xmlSaved) { - result.xmlSaved = modified && isXml; - } - } - - var modified = false; - for (var file : mViewModel.getOpenedFiles()) { - var editor = getEditorForFile(file); - if (editor == null) { - continue; - } - modified = modified || editor.isModified(); - } - - final boolean finalModified = modified; - ThreadUtils.runOnUiThread(() -> mViewModel.setFilesModified(finalModified)); - } - - 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); - } - - private void initializeProject() { - final var projectPath = ProjectManager.INSTANCE.getProjectDirPath(); - if (projectPath == null) { - LOG.error("Cannot initialize project. Project model is null."); + final var future = mBuildService.initializeProject(projectDir.getAbsolutePath()); + future.whenComplete( + (result, error) -> { + final var root = result == null ? null : result.getProject(); + if (result == null || root == null || error != null) { + LOG.error("An error occurred initializing the project with Tooling API", error); + setStatus(getString(R.string.msg_project_initialization_failed)); return; - } + } - final var projectDir = new File(projectPath); - if (!projectDir.exists()) { - LOG.error("Project directory does not exist. Cannot initialize project"); - return; - } + onProjectInitialized(root); + }); + } - ThreadUtils.runOnUiThread( - () -> { - setStatus(getString(R.string.msg_initializing_project)); - mBinding.buildProgressIndicator.setVisibility(View.VISIBLE); - }); + public void setStatus(final CharSequence text) { + setStatus(text, Gravity.CENTER); + } - final var future = mBuildService.initializeProject(projectDir.getAbsolutePath()); + protected void onProjectInitialized(IdeGradleProject root) { + ProjectManager.INSTANCE.setRootProject(root); + ProjectManager.INSTANCE.notifyProjectUpdate(); + ThreadUtils.runOnUiThread( + () -> { + setStatus(getString(R.string.msg_project_initialized)); + mBinding.buildProgressIndicator.setVisibility(View.GONE); + if (mFindInProjectDialog != null && mFindInProjectDialog.isShowing()) { + mFindInProjectDialog.dismiss(); + } + + mFindInProjectDialog = null; // Create the dialog again if needed + }); + } + + public void setStatus(final CharSequence text, @GravityInt int gravity) { + try { + runOnUiThread( + () -> { + mBinding.bottomSheet.statusText.setGravity(gravity); + mBinding.bottomSheet.statusText.setText(text); + }); + } catch (Throwable th) { + LOG.error("Failed to update status text", th); + } + } + + public void assembleDebug(boolean installApk) { + execTasks(installApk ? installableTaskResultConsumer("debug") : null, "assembleDebug"); + } + + @SuppressWarnings("UnusedReturnValue") + @NonNull + public CompletableFuture execTasks( + Consumer resultHandler, String... tasks) { + ThreadUtils.runOnUiThread( + () -> appendBuildOut("Executing tasks: " + TextUtils.join(", ", tasks))); + return mBuildService + .executeTasks(tasks) + .whenComplete( + ((executionResult, throwable) -> { + if (executionResult == null || throwable != null) { + LOG.error("Tasks failed to execute", TextUtils.join(", ", tasks)); + } + + if (resultHandler != null) { + resultHandler.accept(executionResult); + } + })); + } + + public Consumer installableTaskResultConsumer(@NonNull String variantName) { + return task -> { + if (task != null) { + LOG.debug("Installing APK(s) for variant:", variantName); + // TODO Handle multiple application modules + final var future = ProjectManager.INSTANCE.getApplicationModule(); future.whenComplete( - (result, error) -> { - final var root = result == null ? null : result.getProject(); - if (result == null || root == null || error != null) { - LOG.error( - "An error occurred initializing the project with Tooling API", - error); - setStatus(getString(R.string.msg_project_initialization_failed)); - return; - } + (app, error) -> { + if (app == null) { + return; + } - onProjectInitialized(root); - }); - } + final var variants = app.getVariants(); + final var foundVariant = + variants.stream() + .filter(variant -> variant.getName().equals(variantName)) + .findFirst(); - public void setStatus(final CharSequence text) { - setStatus(text, Gravity.CENTER); - } - - protected void onProjectInitialized(IdeGradleProject root) { - mRootProject = root; - ProjectManager.INSTANCE.setRootProject(mRootProject); - ProjectManager.INSTANCE.notifyProjectUpdate(); - ThreadUtils.runOnUiThread( - () -> { - setStatus(getString(R.string.msg_project_initialized)); - mBinding.buildProgressIndicator.setVisibility(View.GONE); - if (mFindInProjectDialog != null && mFindInProjectDialog.isShowing()) { - mFindInProjectDialog.dismiss(); - } - - mFindInProjectDialog = null; // Create the dialog again if needed - }); - } - - public void setStatus(final CharSequence text, @GravityInt int gravity) { - try { - runOnUiThread( - () -> { - mBinding.bottomSheet.statusText.setGravity(gravity); - mBinding.bottomSheet.statusText.setText(text); - }); - } catch (Throwable th) { - LOG.error("Failed to update status text", th); - } - } - - public void assembleDebug(boolean installApk) { - execTasks(installApk ? installableTaskResultConsumer("debug") : null, "assembleDebug"); - } - - @SuppressWarnings("UnusedReturnValue") - @NonNull - public CompletableFuture execTasks( - Consumer resultHandler, String... tasks) { - ThreadUtils.runOnUiThread( - () -> appendBuildOut("Executing tasks: " + TextUtils.join(", ", tasks))); - return mBuildService - .executeTasks(tasks) - .whenComplete( - ((executionResult, throwable) -> { - if (executionResult == null || throwable != null) { - LOG.error("Tasks failed to execute", TextUtils.join(", ", tasks)); - } - - if (resultHandler != null) { - resultHandler.accept(executionResult); - } - })); - } - - public Consumer installableTaskResultConsumer( - @NonNull String variantName) { - return task -> { - if (task != null) { - LOG.debug("Installing APK(s) for variant:", variantName); - // TODO Handle multiple application modules - final var app = mRootProject.findFirstAndroidModule(); - if (app == null) { - LOG.error("No application module found in root project. Cannot install APKs"); - return; + if (foundVariant.isPresent()) { + final var variant = foundVariant.get(); + final var main = variant.getMainArtifact(); + final var outputListingFile = main.getAssembleTaskOutputListingFile(); + if (outputListingFile == null) { + LOG.error("No output listing file provided with project model"); + return; } - final var variants = app.getVariants(); - final var foundVariant = - variants.stream() - .filter(variant -> variant.getName().equals(variantName)) - .findFirst(); - - if (foundVariant.isPresent()) { - final var variant = foundVariant.get(); - final var main = variant.getMainArtifact(); - final var outputListingFile = main.getAssembleTaskOutputListingFile(); - if (outputListingFile == null) { - LOG.error("No output listing file provided with project model"); - return; - } - - final var apkFile = ApkMetadata.findApkFile(outputListingFile); - if (apkFile == null) { - LOG.error( - "No apk file specified in output listing file:", outputListingFile); - return; - } - - if (!apkFile.exists()) { - LOG.error( - "APK file specified in output listing file does not exist!", - apkFile); - return; - } - - install(apkFile); - } else { - LOG.error( - "No", - variantName, - "variant found in application module", - app.getProjectPath()); + final var apkFile = ApkMetadata.findApkFile(outputListingFile); + if (apkFile == null) { + LOG.error("No apk file specified in output listing file:", outputListingFile); + return; } - } - }; + + if (!apkFile.exists()) { + LOG.error("APK file specified in output listing file does not exist!", apkFile); + return; + } + + install(apkFile); + } else { + LOG.error( + "No", variantName, "variant found in application module", app.getProjectPath()); + } + }); + } + }; + } + + public void appendBuildOut(final String str) { + final var frag = bottomSheetTabAdapter.getBuildOutputFragment(); + + if (frag != null) { + frag.appendOutput(str); } - - public void appendBuildOut(final String str) { - final var frag = bottomSheetTabAdapter.getBuildOutputFragment(); - - if (frag != null) { - frag.appendOutput(str); - } - } - - public void install(@NonNull File apk) { - ThreadUtils.runOnUiThread( - () -> { - LOG.debug("Installing APK:", apk); - if (apk.exists()) { - Intent i = IntentUtils.getInstallAppIntent(apk); - if (i != null) { - startActivity(i); - } else { - getApp().toast( - R.string.msg_apk_install_intent_failed, - Toaster.Type.ERROR); - } - } else { - LOG.error("APK file does not exist!"); - } - }); - } - - public void assembleRelease() { - execTasks(null, "assembleRelease"); - } - - public void build() { - execTasks(null, "build"); - } - - public void clean() { - execTasks(null, "clean"); - } - - public void bundle() { - execTasks(null, "bundle"); - } - - public void lint() { - execTasks(null, "lint"); - } - - public void lintDebug() { - execTasks(null, "lintDebug"); - } - - public void lintRelease() { - execTasks(null, "lintRelease"); - } - - public void cleanAndRebuild() { - execTasks(null, "clean", "build"); - } - - ///////////////////////////////////////////////// - ////////////// PRIVATE APIS ///////////////////// - ///////////////////////////////////////////////// - - public AlertDialog getFindInProjectDialog() { - return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog; - } - - @Override - public void onOptionsClick(SheetOption option) { - if (mFileOptionsHandler != null) { - mFileOptionsHandler.onOptionsClick(option); - } - } - - public GradleBuildService getBuildService() { - return mBuildService; - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - setSupportActionBar(mBinding.editorToolbar); - - mViewModel = new ViewModelProvider(this).get(EditorViewModel.class); - getProjectFromIntent(); - - mFileTreeFragment = FileTreeFragment.newInstance(); - mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true); - - setupDrawerToggle(); - loadFragment(mFileTreeFragment); - - symbolInput = new SymbolInputView(this); - mBinding.bottomSheet.textContainer.addView( - symbolInput, 0, new ViewGroup.LayoutParams(-1, -2)); - mBinding.tabs.addOnTabSelectedListener(this); - - setupEditorBottomSheet(); - createQuickActions(); - - mBuildEventListener.setActivity(this); - mFileOptionsHandler = new FileOptionsHandler(this); - - startServices(); - - KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged()); - registerLogReceiver(); - setupContainers(); - setupDiagnosticInfo(); - - mUIDesignerLauncher = - registerForActivityResult( - new ActivityResultContracts.StartActivityForResult(), - this::onGetUIDesignerResult); - - EditorActivityActions.register(this); - } - - @Override - protected View bindLayout() { - mBinding = ActivityEditorBinding.inflate(getLayoutInflater()); - mDiagnosticInfoBinding = mBinding.diagnosticInfo; - return mBinding.getRoot(); - } - - @Override - protected void onPause() { - super.onPause(); - dispatchOnPauseToEditors(); - EditorActivityActions.clear(); - - if (mFileTreeFragment != null) { - mFileTreeFragment.saveTreeState(); - } - } - - @Override - protected void onResume() { - super.onResume(); - - EditorActivityActions.register(this); - - try { - checkForCompilerModule(); - dispatchOnResumeToEditors(); - - if (mFileTreeFragment != null) { - mFileTreeFragment.listProjectFiles(); - } - } catch (Throwable th) { - LOG.error("Failed to update files list", th); - getApp().toast(R.string.msg_failed_list_files, Toaster.Type.ERROR); - } - } - - @SuppressWarnings("deprecation") - private void checkForCompilerModule() { - if (!Environment.isCompilerModuleInstalled()) { - final var pd = - ProgressDialog.show( - this, - getString(R.string.title_compiler_module_install), - getString(R.string.msg_compiler_module_install), - true, - false); - - final CompletableFuture future = - CompletableFuture.supplyAsync( - () -> { - final var tmpModule = - new File(Environment.TMP_DIR, "compiler-module.zip"); - if (!ResourceUtils.copyFileFromAssets( - ToolsManager.getCommonAsset("compiler-module.zip"), - tmpModule.getAbsolutePath())) { - throw new CompletionException( - new RuntimeException( - "Unable to copy compiler-module.zip")); - } - - try { - ZipUtils.unzipFile(tmpModule, Environment.COMPILER_MODULE); - } catch (Throwable e) { - throw new CompletionException(e); - } - - if (!Environment.isCompilerModuleInstalled()) { - throw new CompletionException( - new RuntimeException("Unknown error")); - } - - try { - FileUtils.delete(tmpModule); - } catch (Exception e) { - // ignored - } - - return true; - }); - - future.whenComplete( - (result, error) -> { - pd.dismiss(); - - if (error != null) { - showCompilerModuleInstallError(error); - return; - } - - getApp().toast( - getString(R.string.msg_compiler_module_installed), - Toaster.Type.SUCCESS); - }); - } - } - - private void dispatchOnResumeToEditors() { - CompletableFuture.runAsync( - () -> { - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - final var editor = getEditorAtIndex(i); - if (editor != null) { - editor.onResume(); - } - } - }); - } - - private void showCompilerModuleInstallError(Throwable error) { - final var stacktrace = ThrowableUtils.getFullStackTrace(error); - final var builder = DialogUtils.newMaterialDialogBuilder(this); - builder.setTitle(R.string.title_installation_failed); - builder.setMessage(getString(R.string.msg_compiler_module_install_failed, stacktrace)); - builder.setCancelable(false); - builder.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()); - builder.setNegativeButton( - R.string.copy, - (dialog, which) -> { - ClipboardUtils.copyText(stacktrace); - dialog.dismiss(); - }); - builder.show(); - } - - private void dispatchOnPauseToEditors() { - CompletableFuture.runAsync( - () -> { - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - final var editor = getEditorAtIndex(i); - if (editor != null) { - editor.onPause(); - } - } - }); - } - - @Override - protected void onDestroy() { - closeProject(false); - try { - unregisterReceiver(mLogReceiver); - } catch (Throwable th) { - LOG.error("Failed to unregister LogReceiver", th); - } - unbindService(mGradleServiceConnection); - super.onDestroy(); - } - - private void setupDrawerToggle() { - ActionBarDrawerToggle toggle = - new ActionBarDrawerToggle( - this, - mBinding.editorDrawerLayout, - mBinding.editorToolbar, - R.string.app_name, - R.string.app_name); - mBinding.editorDrawerLayout.addDrawerListener(toggle); - mBinding.startNav.setNavigationItemSelectedListener(this); - toggle.syncState(); - } - - private void setupEditorBottomSheet() { - bottomSheetTabAdapter = new EditorBottomSheetTabAdapter(this); - mBinding.bottomSheet.pager.setAdapter(bottomSheetTabAdapter); - - final var mediator = - new TabLayoutMediator( - mBinding.bottomSheet.tabs, - mBinding.bottomSheet.pager, - true, - true, - (tab, position) -> tab.setText(bottomSheetTabAdapter.getTitle(position))); - - mediator.attach(); - mBinding.bottomSheet.pager.setUserInputEnabled(false); - mBinding.bottomSheet.pager.setOffscreenPageLimit( - bottomSheetTabAdapter.getItemCount() - 1); // DO not remove any views - - mEditorBottomSheet = - (EditorBottomSheetBehavior) - EditorBottomSheetBehavior.from(mBinding.bottomSheet.getRoot()); - mEditorBottomSheet.setBinding(mBinding.bottomSheet); - mEditorBottomSheet.addBottomSheetCallback( - new BottomSheetBehavior.BottomSheetCallback() { - @Override - public void onStateChanged(@NonNull View bottomSheet, int newState) { - mBinding.bottomSheet.textContainer.setVisibility( - newState == BottomSheetBehavior.STATE_EXPANDED - ? View.INVISIBLE - : View.VISIBLE); - - if (newState == BottomSheetBehavior.STATE_EXPANDED) { - final var editor = getCurrentEditor(); - if (editor != null && editor.getEditor() != null) { - editor.getEditor().ensureWindowsDismissed(); - } - } - } - - @Override - public void onSlide(@NonNull View bottomSheet, float slideOffset) { - mBinding.bottomSheet.textContainer.setAlpha(1f - slideOffset); - } - }); - - mBinding.bottomSheet.tabs.addOnTabSelectedListener( - new TabLayout.OnTabSelectedListener() { - - @Override - public void onTabSelected(TabLayout.Tab tab) { - final var fragment = - bottomSheetTabAdapter.getFragmentAtIndex(tab.getPosition()); - if (fragment instanceof NonEditableEditorFragment) { - mBinding.bottomSheet.clearFab.show(); - mBinding.bottomSheet.shareOutputFab.show(); - } else { - mBinding.bottomSheet.clearFab.hide(); - mBinding.bottomSheet.shareOutputFab.hide(); - } - } - - @Override - public void onTabUnselected(TabLayout.Tab tab) {} - - @Override - public void onTabReselected(TabLayout.Tab tab) {} - }); - - TooltipCompat.setTooltipText( - mBinding.bottomSheet.clearFab, getString(R.string.title_clear_output)); - mBinding.bottomSheet.clearFab.setOnClickListener( - v -> { - final var fragment = - bottomSheetTabAdapter.getFragmentAtIndex( - mBinding.bottomSheet.tabs.getSelectedTabPosition()); - if (fragment instanceof NonEditableEditorFragment) { - final var editor = (NonEditableEditorFragment) fragment; - if (editor.getEditor() != null) { - editor.getEditor().setText(""); - } - } - }); - - mBinding.bottomSheet.shareOutputFab.setOnClickListener( - v -> { - final var fragment = - bottomSheetTabAdapter.getFragmentAtIndex( - mBinding.bottomSheet.tabs.getSelectedTabPosition()); - if (fragment instanceof NonEditableEditorFragment) { - final var editor = (NonEditableEditorFragment) fragment; - if (editor.getEditor() != null) { - final var text = editor.getEditor().getText(); - final var type = - editor instanceof SimpleOutputFragment - ? "build_output" - : editor instanceof IDELogFragment - ? "ide_logs" - : editor instanceof LogViewFragment - ? "app_logs" - : "unknown_type"; - shareText(text, type); - } - } - }); - - if (!getApp().getPrefManager().getBoolean(KEY_BOTTOM_SHEET_SHOWN) - && mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) { - mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); - new Handler(Looper.getMainLooper()) - .postDelayed( - () -> { - mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); - getApp().getPrefManager().putBoolean(KEY_BOTTOM_SHEET_SHOWN, true); - }, - 1500); - } - } - - @SuppressWarnings("deprecation") - private void shareText(CharSequence text, String type) { - final var pd = - ProgressDialog.show(this, null, getString(R.string.please_wait), true, false); - CompletableFuture.supplyAsync(() -> writeTempFile(text, type)) - .whenComplete( - (result, error) -> { - ThreadUtils.runOnUiThread(pd::dismiss); - if (result == null || error != null) { - LOG.warn("Unable to share output", error); - return; - } - - ThreadUtils.runOnUiThread( - () -> { - shareFile(result); - }); - }); - } - - private void shareFile(File file) { - final var uri = - FileProvider.getUriForFile( - this, BuildConfig.APPLICATION_ID + ".utilcode.fileprovider", file); - final var intent = new Intent(Intent.ACTION_SEND); - intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - intent.setType("text/plain"); - intent.putExtra(Intent.EXTRA_STREAM, uri); - - startActivity(Intent.createChooser(intent, null)); - } - - @NonNull - private File writeTempFile(CharSequence text, String type) { - final var in = new CharSequenceInputStream(text, StandardCharsets.UTF_8); - final var file = new File(getFilesDir(), type + ".txt"); // use a common name to avoid multiple files - if (!FileIOUtils.writeFileFromIS(file, in)) { - LOG.error("Unable to write output of type", type, "to temporary file", file); - throw new RuntimeException(new IOException("Cannot write output to temp file")); - } - - return file; - } - - private void notifyFilesUnsaved(List unsavedEditors, Runnable invokeAfter) { - //noinspection ConstantConditions - final var mapped = - unsavedEditors.stream() - .map(CodeEditorView::getFile) - .filter(Objects::nonNull) - .map(File::getAbsolutePath) - .collect(Collectors.toList()); - final var builder = - DialogUtils.newYesNoDialog( - this, - getString(R.string.title_files_unsaved), // title - getString( - R.string.msg_files_unsaved, - TextUtils.join("\n", mapped)), // message - (dialog, which) -> { // 'yes' click - dialog.dismiss(); - saveAll(true); - invokeAfter.run(); - }, - (dialog, which) -> { // 'no' click - dialog.dismiss(); - // Mark all the files as saved, then try to close them all - for (var editor : unsavedEditors) { - editor.markAsSaved(); - } - - invokeAfter.run(); - }); - builder.show(); - } - - @Contract(pure = true) - private void onGetUIDesignerResult(@NonNull ActivityResult result) { - final var index = mBinding.editorContainer.getDisplayedChild(); - final var editor = getEditorAtIndex(index); - if (editor != null && result.getResultCode() == RESULT_OK) { - final var data = result.getData(); - if (data != null && data.hasExtra(DesignerActivity.KEY_GENERATED_CODE)) { - final var code = data.getStringExtra(DesignerActivity.KEY_GENERATED_CODE); - editor.getEditor().setText(code); - saveAll(); + } + + public void install(@NonNull File apk) { + ThreadUtils.runOnUiThread( + () -> { + LOG.debug("Installing APK:", apk); + if (apk.exists()) { + Intent i = IntentUtils.getInstallAppIntent(apk); + if (i != null) { + startActivity(i); } else { - final var msg = getString(R.string.msg_invalid_designer_result); - getApp().toast(msg, Toaster.Type.ERROR); - LOG.error(msg, "Data returned by UI Designer is null or is invalid."); + getApp().toast(R.string.msg_apk_install_intent_failed, Toaster.Type.ERROR); } - } else { - LOG.error( - "UI Designer returned an invalid result code.", - "Result code: " + result.getResultCode()); - } + } else { + LOG.error("APK file does not exist!"); + } + }); + } + + public void assembleRelease() { + execTasks(null, "assembleRelease"); + } + + public void build() { + execTasks(null, "build"); + } + + public void clean() { + execTasks(null, "clean"); + } + + public void bundle() { + execTasks(null, "bundle"); + } + + public void lint() { + execTasks(null, "lint"); + } + + public void lintDebug() { + execTasks(null, "lintDebug"); + } + + public void lintRelease() { + execTasks(null, "lintRelease"); + } + + public void cleanAndRebuild() { + execTasks(null, "clean", "build"); + } + + ///////////////////////////////////////////////// + ////////////// PRIVATE APIS ///////////////////// + ///////////////////////////////////////////////// + + public AlertDialog getFindInProjectDialog() { + return mFindInProjectDialog == null + ? createFindInProjectDialog() + : mFindInProjectDialog; + } + + @Override + public void onOptionsClick(SheetOption option) { + if (mFileOptionsHandler != null) { + mFileOptionsHandler.onOptionsClick(option); } + } - private void openTerminal() { - final Intent intent = new Intent(this, TerminalActivity.class); - intent.putExtra( - TerminalActivity.KEY_WORKING_DIRECTORY, - Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())); - startActivity(intent); + public GradleBuildService getBuildService() { + return mBuildService; + } + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + setSupportActionBar(mBinding.editorToolbar); + + mViewModel = new ViewModelProvider(this).get(EditorViewModel.class); + getProjectFromIntent(); + + mFileTreeFragment = FileTreeFragment.newInstance(); + mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true); + + setupDrawerToggle(); + loadFragment(mFileTreeFragment); + + symbolInput = new SymbolInputView(this); + mBinding.bottomSheet.textContainer.addView(symbolInput, 0, new ViewGroup.LayoutParams(-1, -2)); + mBinding.tabs.addOnTabSelectedListener(this); + + setupEditorBottomSheet(); + createQuickActions(); + + mBuildEventListener.setActivity(this); + mFileOptionsHandler = new FileOptionsHandler(this); + + startServices(); + + KeyboardUtils.registerSoftInputChangedListener(this, __ -> onSoftInputChanged()); + registerLogReceiver(); + setupContainers(); + setupDiagnosticInfo(); + + mUIDesignerLauncher = + registerForActivityResult( + new ActivityResultContracts.StartActivityForResult(), this::onGetUIDesignerResult); + + EditorActivityActions.register(this); + } + + @Override + protected View bindLayout() { + mBinding = ActivityEditorBinding.inflate(getLayoutInflater()); + mDiagnosticInfoBinding = mBinding.diagnosticInfo; + return mBinding.getRoot(); + } + + @Override + protected void onPause() { + super.onPause(); + dispatchOnPauseToEditors(); + EditorActivityActions.clear(); + + if (mFileTreeFragment != null) { + mFileTreeFragment.saveTreeState(); } + } - private void getProjectFromIntent() { - final var project = getIntent().getStringExtra(EXTRA_PROJECT_PATH); - ProjectManager.INSTANCE.setProjectPath(project); + @Override + protected void onResume() { + super.onResume(); - getApp().getPrefManager() - .setOpenedProject( - Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())); + EditorActivityActions.register(this); - try { - //noinspection ConstantConditions - getSupportActionBar() - .setSubtitle(new File(ProjectManager.INSTANCE.getProjectDirPath()).getName()); - } catch (Throwable th) { - // ignored - } + try { + checkForCompilerModule(); + dispatchOnResumeToEditors(); - CompletableFuture.runAsync( - () -> - ValuesTableFactory.setupWithResDirectories( - getResourceDirectories().stream() - .map(File::new) - .toArray(File[]::new))); + if (mFileTreeFragment != null) { + mFileTreeFragment.listProjectFiles(); + } + } catch (Throwable th) { + LOG.error("Failed to update files list", th); + getApp().toast(R.string.msg_failed_list_files, Toaster.Type.ERROR); } + } - private void setupDiagnosticInfo() { - GradientDrawable gd = new GradientDrawable(); - gd.setShape(GradientDrawable.RECTANGLE); - gd.setColor(0xff212121); - gd.setStroke(1, 0xffffffff); - gd.setCornerRadius(8); - mDiagnosticInfoBinding.getRoot().setBackground(gd); - mDiagnosticInfoBinding.getRoot().setVisibility(View.GONE); - } + @SuppressWarnings("deprecation") + private void checkForCompilerModule() { + if (!Environment.isCompilerModuleInstalled()) { + final var pd = + ProgressDialog.show( + this, + getString(R.string.title_compiler_module_install), + getString(R.string.msg_compiler_module_install), + true, + false); - private void setupContainers() { - handleDiagnosticsResultVisibility(true); - handleSearchResultVisibility(true); - } + final CompletableFuture future = + CompletableFuture.supplyAsync( + () -> { + final var tmpModule = new File(Environment.TMP_DIR, "compiler-module.zip"); + if (!ResourceUtils.copyFileFromAssets( + ToolsManager.getCommonAsset("compiler-module.zip"), + tmpModule.getAbsolutePath())) { + throw new CompletionException( + new RuntimeException("Unable to copy compiler-module.zip")); + } - private void startServices() { + try { + ZipUtils.unzipFile(tmpModule, Environment.COMPILER_MODULE); + } catch (Throwable e) { + throw new CompletionException(e); + } - if (bindService( - new Intent(this, GradleBuildService.class), - mGradleServiceConnection, - Context.BIND_AUTO_CREATE)) { - LOG.info("Bind request for Gradle build service was successful..."); - } else { - LOG.error("Gradle build service doesn't exist or the IDE is not allowed to access it."); - } + if (!Environment.isCompilerModuleInstalled()) { + throw new CompletionException(new RuntimeException("Unknown error")); + } - if (!IDELanguageClientImpl.isInitialized()) { - IDELanguageClientImpl.initialize(this); - } + try { + FileUtils.delete(tmpModule); + } catch (Exception e) { + // ignored + } - initializeLanguageServers(); + return true; + }); - // Actually, we don't need to start FileOptionsHandler - // Because it would work anyway - // But still we do... - mFileOptionsHandler.start(); - } + future.whenComplete( + (result, error) -> { + pd.dismiss(); - private void initializeLanguageServers() { - final var client = IDELanguageClientImpl.getInstance(); - final var javaLanguageServer = getApp().getJavaLanguageServer(); - final var workspaceRoots = new HashSet(); - workspaceRoots.add( - new File(Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())) - .toPath()); - workspaceRoots.add(Environment.HOME.toPath().resolve("logsender")); - - final var params = new InitializeParams(workspaceRoots); - - javaLanguageServer.connectClient(client); - javaLanguageServer.applySettings(JavaServerSettings.getInstance()); - javaLanguageServer.initialize(params); - } - - private void stopServices() { - - if (IDELanguageClientImpl.isInitialized()) { - IDELanguageClientImpl.shutdown(); - } - - shutdownLanguageServers(); - if (mFileOptionsHandler != null) { - mFileOptionsHandler.stop(); - } - } - - private void shutdownLanguageServers() { - final var javaServer = getApp().getJavaLanguageServer(); - javaServer.shutdown(); - } - - private void onSoftInputChanged() { - invalidateOptionsMenu(); - if (KeyboardUtils.isSoftInputVisible(this)) { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.TOP)); - symbolInput.setVisibility(View.VISIBLE); - mBinding.bottomSheet.statusText.setVisibility(View.GONE); - mBinding.bottomSheet.swipeHint.setVisibility(View.GONE); - } else { - TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.BOTTOM)); - symbolInput.setVisibility(View.GONE); - mBinding.bottomSheet.statusText.setVisibility(View.VISIBLE); - mBinding.bottomSheet.swipeHint.setVisibility(View.VISIBLE); - } - } - - private void closeProject(boolean manualFinish) { - stopServices(); - - // Make sure we close files - // This fill further make sure that file contents are not erased. - closeAll( - () -> { - getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT); - - if (manualFinish) { - finish(); - } - }); - } - - private void confirmProjectClose() { - final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this); - builder.setTitle(R.string.title_confirm_project_close); - builder.setMessage(R.string.msg_confirm_project_close); - builder.setNegativeButton(R.string.no, null); - builder.setPositiveButton( - R.string.yes, - (d, w) -> { - d.dismiss(); - closeProject(true); - }); - builder.show(); - } - - @Nullable - private AlertDialog createFindInProjectDialog() { - final var rootProject = ProjectManager.INSTANCE.getRootProject(); - if (rootProject == null) { - LOG.warn("No root project model found. Is the project initialized?"); - getApp().toast(getString(R.string.msg_project_not_initialized), Toaster.Type.ERROR); - return null; - } - - final var moduleDirs = - rootProject.getModules().stream() - .map(IdeGradleProject::getProjectDir) - .collect(Collectors.toList()); - - final List srcDirs = new ArrayList<>(); - final LayoutSearchProjectBinding binding = - LayoutSearchProjectBinding.inflate(getLayoutInflater()); - binding.modulesContainer.removeAllViews(); - for (int i = 0; i < moduleDirs.size(); i++) { - final File module = moduleDirs.get(i); - final File src = new File(module, "src"); - if (!module.exists() || !module.isDirectory() || !src.exists() || !src.isDirectory()) { - continue; + if (error != null) { + showCompilerModuleInstallError(error); + return; } - CheckBox check = new CheckBox(this); - check.setText(module.getName()); - check.setChecked(true); + getApp().toast(getString(R.string.msg_compiler_module_installed), Toaster.Type.SUCCESS); + }); + } + } - LinearLayout.MarginLayoutParams params = new LinearLayout.MarginLayoutParams(-2, -2); - params.bottomMargin = SizeUtils.dp2px(4); - binding.modulesContainer.addView(check, params); + private void dispatchOnResumeToEditors() { + CompletableFuture.runAsync( + () -> { + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + final var editor = getEditorAtIndex(i); + if (editor != null) { + editor.onResume(); + } + } + }); + } - srcDirs.add(src); - } - final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this); - builder.setTitle(R.string.menu_find_project); - builder.setView(binding.getRoot()); - builder.setCancelable(false); - builder.setPositiveButton( - R.string.menu_find, - (dialog, which) -> { - final String text = - Objects.requireNonNull(binding.input.getEditText()) - .getText() - .toString() - .trim(); - if (text.isEmpty()) { - getApp().toast(R.string.msg_empty_search_query, Toaster.Type.ERROR); - return; - } + private void showCompilerModuleInstallError(Throwable error) { + final var stacktrace = ThrowableUtils.getFullStackTrace(error); + final var builder = DialogUtils.newMaterialDialogBuilder(this); + builder.setTitle(R.string.title_installation_failed); + builder.setMessage(getString(R.string.msg_compiler_module_install_failed, stacktrace)); + builder.setCancelable(false); + builder.setPositiveButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()); + builder.setNegativeButton( + R.string.copy, + (dialog, which) -> { + ClipboardUtils.copyText(stacktrace); + dialog.dismiss(); + }); + builder.show(); + } - final List searchDirs = new ArrayList<>(); - for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { - CheckBox check = (CheckBox) binding.modulesContainer.getChildAt(i); - if (check.isChecked()) { - searchDirs.add(srcDirs.get(i)); - } - } + private void dispatchOnPauseToEditors() { + CompletableFuture.runAsync( + () -> { + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + final var editor = getEditorAtIndex(i); + if (editor != null) { + editor.onPause(); + } + } + }); + } - final String extensions = - Objects.requireNonNull(binding.filter.getEditText()) - .getText() - .toString() - .trim(); - final List extensionList = new ArrayList<>(); - if (!extensions.isEmpty()) { - if (extensions.contains("|")) { - for (String str : extensions.split(Pattern.quote("|"))) { - if (str == null || str.trim().isEmpty()) { - continue; - } + @Override + protected void onDestroy() { + closeProject(false); + try { + unregisterReceiver(mLogReceiver); + } catch (Throwable th) { + LOG.error("Failed to unregister LogReceiver", th); + } + unbindService(mGradleServiceConnection); + super.onDestroy(); + } - extensionList.add(str); - } - } else { - extensionList.add(extensions); - } - } + private void setupDrawerToggle() { + ActionBarDrawerToggle toggle = + new ActionBarDrawerToggle( + this, + mBinding.editorDrawerLayout, + mBinding.editorToolbar, + R.string.app_name, + R.string.app_name); + mBinding.editorDrawerLayout.addDrawerListener(toggle); + mBinding.startNav.setNavigationItemSelectedListener(this); + toggle.syncState(); + } - if (searchDirs.isEmpty()) { - getApp().toast(R.string.msg_select_search_modules, Toaster.Type.ERROR); - } else { - dialog.dismiss(); - getProgressSheet(R.string.msg_searching_project) - .show(getSupportFragmentManager(), "search_in_project_progress"); - RecursiveFileSearcher.searchRecursiveAsync( - text, extensionList, searchDirs, this::handleSearchResults); - } - }); - builder.setNegativeButton(android.R.string.cancel, (__, ___) -> __.dismiss()); - mFindInProjectDialog = builder.create(); - return mFindInProjectDialog; + private void setupEditorBottomSheet() { + bottomSheetTabAdapter = new EditorBottomSheetTabAdapter(this); + mBinding.bottomSheet.pager.setAdapter(bottomSheetTabAdapter); + + final var mediator = + new TabLayoutMediator( + mBinding.bottomSheet.tabs, + mBinding.bottomSheet.pager, + true, + true, + (tab, position) -> tab.setText(bottomSheetTabAdapter.getTitle(position))); + + mediator.attach(); + mBinding.bottomSheet.pager.setUserInputEnabled(false); + mBinding.bottomSheet.pager.setOffscreenPageLimit( + bottomSheetTabAdapter.getItemCount() - 1); // DO not remove any views + + mEditorBottomSheet = + (EditorBottomSheetBehavior) + EditorBottomSheetBehavior.from(mBinding.bottomSheet.getRoot()); + mEditorBottomSheet.setBinding(mBinding.bottomSheet); + mEditorBottomSheet.addBottomSheetCallback( + new BottomSheetBehavior.BottomSheetCallback() { + @Override + public void onStateChanged(@NonNull View bottomSheet, int newState) { + mBinding.bottomSheet.textContainer.setVisibility( + newState == BottomSheetBehavior.STATE_EXPANDED ? View.INVISIBLE : View.VISIBLE); + + if (newState == BottomSheetBehavior.STATE_EXPANDED) { + final var editor = getCurrentEditor(); + if (editor != null && editor.getEditor() != null) { + editor.getEditor().ensureWindowsDismissed(); + } + } + } + + @Override + public void onSlide(@NonNull View bottomSheet, float slideOffset) { + mBinding.bottomSheet.textContainer.setAlpha(1f - slideOffset); + } + }); + + mBinding.bottomSheet.tabs.addOnTabSelectedListener( + new TabLayout.OnTabSelectedListener() { + + @Override + public void onTabSelected(TabLayout.Tab tab) { + final var fragment = bottomSheetTabAdapter.getFragmentAtIndex(tab.getPosition()); + if (fragment instanceof NonEditableEditorFragment) { + mBinding.bottomSheet.clearFab.show(); + mBinding.bottomSheet.shareOutputFab.show(); + } else { + mBinding.bottomSheet.clearFab.hide(); + mBinding.bottomSheet.shareOutputFab.hide(); + } + } + + @Override + public void onTabUnselected(TabLayout.Tab tab) {} + + @Override + public void onTabReselected(TabLayout.Tab tab) {} + }); + + TooltipCompat.setTooltipText( + mBinding.bottomSheet.clearFab, getString(R.string.title_clear_output)); + mBinding.bottomSheet.clearFab.setOnClickListener( + v -> { + final var fragment = + bottomSheetTabAdapter.getFragmentAtIndex( + mBinding.bottomSheet.tabs.getSelectedTabPosition()); + if (fragment instanceof NonEditableEditorFragment) { + final var editor = (NonEditableEditorFragment) fragment; + if (editor.getEditor() != null) { + editor.getEditor().setText(""); + } + } + }); + + mBinding.bottomSheet.shareOutputFab.setOnClickListener( + v -> { + final var fragment = + bottomSheetTabAdapter.getFragmentAtIndex( + mBinding.bottomSheet.tabs.getSelectedTabPosition()); + if (fragment instanceof NonEditableEditorFragment) { + final var editor = (NonEditableEditorFragment) fragment; + if (editor.getEditor() != null) { + final var text = editor.getEditor().getText(); + final var type = + editor instanceof SimpleOutputFragment + ? "build_output" + : editor instanceof IDELogFragment + ? "ide_logs" + : editor instanceof LogViewFragment ? "app_logs" : "unknown_type"; + shareText(text, type); + } + } + }); + + if (!getApp().getPrefManager().getBoolean(KEY_BOTTOM_SHEET_SHOWN) + && mEditorBottomSheet.getState() != BottomSheetBehavior.STATE_EXPANDED) { + mEditorBottomSheet.setState(BottomSheetBehavior.STATE_EXPANDED); + new Handler(Looper.getMainLooper()) + .postDelayed( + () -> { + mEditorBottomSheet.setState(BottomSheetBehavior.STATE_COLLAPSED); + getApp().getPrefManager().putBoolean(KEY_BOTTOM_SHEET_SHOWN, true); + }, + 1500); + } + } + + @SuppressWarnings("deprecation") + private void shareText(CharSequence text, String type) { + final var pd = ProgressDialog.show(this, null, getString(R.string.please_wait), true, false); + CompletableFuture.supplyAsync(() -> writeTempFile(text, type)) + .whenComplete( + (result, error) -> { + ThreadUtils.runOnUiThread(pd::dismiss); + if (result == null || error != null) { + LOG.warn("Unable to share output", error); + return; + } + + ThreadUtils.runOnUiThread( + () -> { + shareFile(result); + }); + }); + } + + private void shareFile(File file) { + final var uri = + FileProvider.getUriForFile( + this, BuildConfig.APPLICATION_ID + ".utilcode.fileprovider", file); + final var intent = new Intent(Intent.ACTION_SEND); + intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + intent.setType("text/plain"); + intent.putExtra(Intent.EXTRA_STREAM, uri); + + startActivity(Intent.createChooser(intent, null)); + } + + @NonNull + private File writeTempFile(CharSequence text, String type) { + final var in = new CharSequenceInputStream(text, StandardCharsets.UTF_8); + final var file = + new File(getFilesDir(), type + ".txt"); // use a common name to avoid multiple files + if (!FileIOUtils.writeFileFromIS(file, in)) { + LOG.error("Unable to write output of type", type, "to temporary file", file); + throw new RuntimeException(new IOException("Cannot write output to temp file")); } - private void registerLogReceiver() { - IntentFilter filter = new IntentFilter(); - filter.addAction(LogReceiver.APPEND_LOG); - registerReceiver(mLogReceiver, filter); + return file; + } + + private void notifyFilesUnsaved(List unsavedEditors, Runnable invokeAfter) { + //noinspection ConstantConditions + final var mapped = + unsavedEditors.stream() + .map(CodeEditorView::getFile) + .filter(Objects::nonNull) + .map(File::getAbsolutePath) + .collect(Collectors.toList()); + final var builder = + DialogUtils.newYesNoDialog( + this, + getString(R.string.title_files_unsaved), // title + getString(R.string.msg_files_unsaved, TextUtils.join("\n", mapped)), // message + (dialog, which) -> { // 'yes' click + dialog.dismiss(); + saveAll(true); + invokeAfter.run(); + }, + (dialog, which) -> { // 'no' click + dialog.dismiss(); + // Mark all the files as saved, then try to close them all + for (var editor : unsavedEditors) { + editor.markAsSaved(); + } + + invokeAfter.run(); + }); + builder.show(); + } + + @Contract(pure = true) + private void onGetUIDesignerResult(@NonNull ActivityResult result) { + final var index = mBinding.editorContainer.getDisplayedChild(); + final var editor = getEditorAtIndex(index); + if (editor != null && result.getResultCode() == RESULT_OK) { + final var data = result.getData(); + if (data != null && data.hasExtra(DesignerActivity.KEY_GENERATED_CODE)) { + final var code = data.getStringExtra(DesignerActivity.KEY_GENERATED_CODE); + editor.getEditor().setText(code); + saveAll(); + } else { + final var msg = getString(R.string.msg_invalid_designer_result); + getApp().toast(msg, Toaster.Type.ERROR); + LOG.error(msg, "Data returned by UI Designer is null or is invalid."); + } + } else { + LOG.error( + "UI Designer returned an invalid result code.", "Result code: " + result.getResultCode()); + } + } + + private void openTerminal() { + final Intent intent = new Intent(this, TerminalActivity.class); + intent.putExtra( + TerminalActivity.KEY_WORKING_DIRECTORY, + Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())); + startActivity(intent); + } + + private void getProjectFromIntent() { + final var project = getIntent().getStringExtra(EXTRA_PROJECT_PATH); + ProjectManager.INSTANCE.setProjectPath(project); + + getApp() + .getPrefManager() + .setOpenedProject(Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())); + + try { + //noinspection ConstantConditions + getSupportActionBar() + .setSubtitle(new File(ProjectManager.INSTANCE.getProjectDirPath()).getName()); + } catch (Throwable th) { + // ignored } - private void showNeedHelpDialog() { - MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this); - builder.setTitle(R.string.need_help); - builder.setMessage(R.string.msg_need_help); - builder.setPositiveButton(android.R.string.ok, null); - builder.create().show(); + getResourceDirs() + .thenAccept( + dirs -> { + dirs.removeIf(Objects::isNull); + ValuesTableFactory.setupWithResDirectories(dirs.toArray(new File[0])); + }); + } + + private void setupDiagnosticInfo() { + GradientDrawable gd = new GradientDrawable(); + gd.setShape(GradientDrawable.RECTANGLE); + gd.setColor(0xff212121); + gd.setStroke(1, 0xffffffff); + gd.setCornerRadius(8); + mDiagnosticInfoBinding.getRoot().setBackground(gd); + mDiagnosticInfoBinding.getRoot().setVisibility(View.GONE); + } + + private void setupContainers() { + handleDiagnosticsResultVisibility(true); + handleSearchResultVisibility(true); + } + + private void startServices() { + + if (bindService( + new Intent(this, GradleBuildService.class), + mGradleServiceConnection, + Context.BIND_AUTO_CREATE)) { + LOG.info("Bind request for Gradle build service was successful..."); + } else { + LOG.error("Gradle build service doesn't exist or the IDE is not allowed to access it."); } - private void createQuickActions() { - ActionItem closeThis = - new ActionItem( - ACTION_ID_CLOSE, - getString(R.string.action_closeThis), - R.drawable.ic_close_this); - ActionItem closeOthers = - new ActionItem( - ACTION_ID_OTHERS, - getString(R.string.action_closeOthers), - R.drawable.ic_close_others); - ActionItem closeAll = - new ActionItem( - ACTION_ID_ALL, - getString(R.string.action_closeAll), - R.drawable.ic_close_all); - mTabCloseAction = new QuickAction(this, QuickAction.HORIZONTAL); - mTabCloseAction.addActionItem(closeThis, closeOthers, closeAll); - mTabCloseAction.setColorRes(R.color.tabAction_background); - mTabCloseAction.setTextColorRes(R.color.tabAction_text); - - mTabCloseAction.setOnActionItemClickListener( - (item) -> { - final int id = item.getActionId(); - if (getApp().getPrefManager() - .getBoolean(PreferenceManager.KEY_EDITOR_AUTO_SAVE, false)) { - saveAll(); - } - - if (id == ACTION_ID_CLOSE) { - closeFile(mBinding.tabs.getSelectedTabPosition()); - } - - if (id == ACTION_ID_OTHERS) { - closeOthers(); - } - - if (id == ACTION_ID_ALL) { - closeAll(); - } - }); + if (!IDELanguageClientImpl.isInitialized()) { + IDELanguageClientImpl.initialize(this); } + + initializeLanguageServers(); + + // Actually, we don't need to start FileOptionsHandler + // Because it would work anyway + // But still we do... + mFileOptionsHandler.start(); + } + + private void initializeLanguageServers() { + final var client = IDELanguageClientImpl.getInstance(); + final var javaLanguageServer = getApp().getJavaLanguageServer(); + final var workspaceRoots = new HashSet(); + workspaceRoots.add( + new File(Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDirPath())).toPath()); + workspaceRoots.add(Environment.HOME.toPath().resolve("logsender")); + + final var params = new InitializeParams(workspaceRoots); + + javaLanguageServer.connectClient(client); + javaLanguageServer.applySettings(JavaServerSettings.getInstance()); + javaLanguageServer.initialize(params); + } + + private void stopServices() { + + if (IDELanguageClientImpl.isInitialized()) { + IDELanguageClientImpl.shutdown(); + } + + shutdownLanguageServers(); + if (mFileOptionsHandler != null) { + mFileOptionsHandler.stop(); + } + } + + private void shutdownLanguageServers() { + final var javaServer = getApp().getJavaLanguageServer(); + javaServer.shutdown(); + } + + private void onSoftInputChanged() { + invalidateOptionsMenu(); + if (KeyboardUtils.isSoftInputVisible(this)) { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.TOP)); + symbolInput.setVisibility(View.VISIBLE); + mBinding.bottomSheet.statusText.setVisibility(View.GONE); + mBinding.bottomSheet.swipeHint.setVisibility(View.GONE); + } else { + TransitionManager.beginDelayedTransition(mBinding.getRoot(), new Slide(Gravity.BOTTOM)); + symbolInput.setVisibility(View.GONE); + mBinding.bottomSheet.statusText.setVisibility(View.VISIBLE); + mBinding.bottomSheet.swipeHint.setVisibility(View.VISIBLE); + } + } + + private void closeProject(boolean manualFinish) { + stopServices(); + + // Make sure we close files + // This fill further make sure that file contents are not erased. + closeAll( + () -> { + getApp().getPrefManager().setOpenedProject(PreferenceManager.NO_OPENED_PROJECT); + + if (manualFinish) { + finish(); + } + }); + } + + private void confirmProjectClose() { + final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this); + builder.setTitle(R.string.title_confirm_project_close); + builder.setMessage(R.string.msg_confirm_project_close); + builder.setNegativeButton(R.string.no, null); + builder.setPositiveButton( + R.string.yes, + (d, w) -> { + d.dismiss(); + closeProject(true); + }); + builder.show(); + } + + @Nullable + private AlertDialog createFindInProjectDialog() { + final var rootProject = ProjectManager.INSTANCE.getRootProject(); + if (rootProject == null) { + LOG.warn("No root project model found. Is the project initialized?"); + getApp().toast(getString(R.string.msg_project_not_initialized), Toaster.Type.ERROR); + return null; + } + + List moduleDirs; + try { + moduleDirs = + rootProject.listModules().get().stream() + .map(SimpleModuleData::getProjectDir) + .collect(Collectors.toList()); + } catch (Throwable e) { + StudioApp.getInstance().toast(getString(R.string.msg_no_modules), Toaster.Type.ERROR); + moduleDirs = Collections.emptyList(); + } + + return createFindInProjectDialog (moduleDirs); + } + + private AlertDialog createFindInProjectDialog (List moduleDirs) { + final List srcDirs = new ArrayList<>(); + final LayoutSearchProjectBinding binding = + LayoutSearchProjectBinding.inflate(getLayoutInflater()); + binding.modulesContainer.removeAllViews(); + for (int i = 0; i < moduleDirs.size(); i++) { + final File module = moduleDirs.get(i); + final File src = new File(module, "src"); + if (!module.exists() || !module.isDirectory() || !src.exists() || !src.isDirectory()) { + continue; + } + + CheckBox check = new CheckBox(this); + check.setText(module.getName()); + check.setChecked(true); + + LinearLayout.MarginLayoutParams params = new LinearLayout.MarginLayoutParams(-2, -2); + params.bottomMargin = SizeUtils.dp2px(4); + binding.modulesContainer.addView(check, params); + + srcDirs.add(src); + } + + final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this); + builder.setTitle(R.string.menu_find_project); + builder.setView(binding.getRoot()); + builder.setCancelable(false); + builder.setPositiveButton( + R.string.menu_find, + (dialog, which) -> { + final String text = + Objects.requireNonNull(binding.input.getEditText()).getText().toString().trim(); + if (text.isEmpty()) { + getApp().toast(R.string.msg_empty_search_query, Toaster.Type.ERROR); + return; + } + + final List searchDirs = new ArrayList<>(); + for (int i = 0; i < mViewModel.getOpenedFileCount(); i++) { + CheckBox check = (CheckBox) binding.modulesContainer.getChildAt(i); + if (check.isChecked()) { + searchDirs.add(srcDirs.get(i)); + } + } + + final String extensions = + Objects.requireNonNull(binding.filter.getEditText()) + .getText() + .toString() + .trim(); + final List extensionList = new ArrayList<>(); + if (!extensions.isEmpty()) { + if (extensions.contains("|")) { + for (String str : extensions.split(Pattern.quote("|"))) { + if (str == null || str.trim().isEmpty()) { + continue; + } + + extensionList.add(str); + } + } else { + extensionList.add(extensions); + } + } + + if (searchDirs.isEmpty()) { + getApp().toast(R.string.msg_select_search_modules, Toaster.Type.ERROR); + } else { + dialog.dismiss(); + getProgressSheet(R.string.msg_searching_project) + .show(getSupportFragmentManager(), "search_in_project_progress"); + RecursiveFileSearcher.searchRecursiveAsync( + text, extensionList, searchDirs, this::handleSearchResults); + } + }); + builder.setNegativeButton(android.R.string.cancel, (__, ___) -> __.dismiss()); + mFindInProjectDialog = builder.create(); + return mFindInProjectDialog; + } + + private void registerLogReceiver() { + IntentFilter filter = new IntentFilter(); + filter.addAction(LogReceiver.APPEND_LOG); + registerReceiver(mLogReceiver, filter); + } + + private void showNeedHelpDialog() { + MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this); + builder.setTitle(R.string.need_help); + builder.setMessage(R.string.msg_need_help); + builder.setPositiveButton(android.R.string.ok, null); + builder.create().show(); + } + + private void createQuickActions() { + ActionItem closeThis = + new ActionItem( + ACTION_ID_CLOSE, getString(R.string.action_closeThis), R.drawable.ic_close_this); + ActionItem closeOthers = + new ActionItem( + ACTION_ID_OTHERS, getString(R.string.action_closeOthers), R.drawable.ic_close_others); + ActionItem closeAll = + new ActionItem(ACTION_ID_ALL, getString(R.string.action_closeAll), R.drawable.ic_close_all); + mTabCloseAction = new QuickAction(this, QuickAction.HORIZONTAL); + mTabCloseAction.addActionItem(closeThis, closeOthers, closeAll); + mTabCloseAction.setColorRes(R.color.tabAction_background); + mTabCloseAction.setTextColorRes(R.color.tabAction_text); + + mTabCloseAction.setOnActionItemClickListener( + (item) -> { + final int id = item.getActionId(); + if (getApp().getPrefManager().getBoolean(PreferenceManager.KEY_EDITOR_AUTO_SAVE, false)) { + saveAll(); + } + + if (id == ACTION_ID_CLOSE) { + closeFile(mBinding.tabs.getSelectedTabPosition()); + } + + if (id == ACTION_ID_OTHERS) { + closeOthers(); + } + + if (id == ACTION_ID_ALL) { + closeAll(); + } + }); + } } diff --git a/app/src/main/java/com/itsaky/androidide/projects/CachingProject.kt b/app/src/main/java/com/itsaky/androidide/projects/CachingProject.kt new file mode 100644 index 000000000..de722c6d2 --- /dev/null +++ b/app/src/main/java/com/itsaky/androidide/projects/CachingProject.kt @@ -0,0 +1,117 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.projects + +import com.itsaky.androidide.tooling.api.IProject +import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData +import com.itsaky.androidide.tooling.api.model.IdeAndroidModule +import com.itsaky.androidide.tooling.api.model.IdeGradleProject +import com.itsaky.androidide.tooling.api.model.IdeGradleTask +import com.itsaky.androidide.utils.ILogger +import java.io.File +import java.util.concurrent.* + +/** + * A project which lazily caches some required properties of the given project. + * + * @author Akash Yadav + */ +class CachingProject(val project: IProject) : IProject { + + private val log = ILogger.newInstance(javaClass.simpleName) + + private val mName: String by lazy { this.project.name.get() } + private val mDescription: String by lazy { this.project.description.get() } + private val mProjectPath: String by lazy { this.project.projectPath.get() } + private val mProjectDir: File by lazy { this.project.projectDir.get() } + private val mBuildDir: File by lazy { this.project.buildDir.get() } + private val mBuildScript: File by lazy { this.project.buildScript.get() } + + private var mFirstAppModule: IdeAndroidModule? = null + private val mModules: MutableList = mutableListOf() + + override fun getName(): CompletableFuture { + return CompletableFuture.completedFuture(this.mName) + } + + override fun getDescription(): CompletableFuture { + return CompletableFuture.completedFuture(mDescription) + } + + override fun getProjectPath(): CompletableFuture { + return CompletableFuture.completedFuture(mProjectPath) + } + + override fun getProjectDir(): CompletableFuture { + return CompletableFuture.completedFuture(mProjectDir) + } + + override fun getBuildDir(): CompletableFuture { + return CompletableFuture.completedFuture(mBuildDir) + } + + override fun getBuildScript(): CompletableFuture { + return CompletableFuture.completedFuture(mBuildScript) + } + + override fun getTasks(): CompletableFuture> { + return this.project.tasks + } + + override fun getModules(): CompletableFuture> { + return this.project.modules + } + + override fun listModules(): CompletableFuture> { + return if (this.mModules.isNotEmpty()) { + CompletableFuture.completedFuture(mModules) + } else { + this.project.listModules().whenComplete { modules, err -> + if (err != null || modules == null || modules.isEmpty()) { + log.warn("Unable to fetch module data", err) + return@whenComplete + } + + this.mModules.clear() + this.mModules.addAll(modules.filterNotNull()) + } + } + } + + override fun findByPath(path: String): CompletableFuture { + return this.project.findByPath(path) + } + + override fun findAndroidModules(): CompletableFuture> { + return this.project.findAndroidModules() + } + + override fun findFirstAndroidModule(): CompletableFuture { + return this.project.findFirstAndroidModule() + } + + override fun findFirstAndroidAppModule(): CompletableFuture { + if (mFirstAppModule != null) { + return CompletableFuture.completedFuture(mFirstAppModule) + } + + return this.project.findFirstAndroidAppModule().whenComplete { module, _ -> + this.mFirstAppModule = module + } + } +} diff --git a/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt b/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt index 47db018c1..a3573153b 100644 --- a/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt +++ b/app/src/main/java/com/itsaky/androidide/projects/ProjectManager.kt @@ -21,29 +21,50 @@ import com.android.builder.model.v2.ide.LibraryType.JAVA_LIBRARY import com.android.builder.model.v2.ide.LibraryType.PROJECT import com.itsaky.androidide.app.StudioApp import com.itsaky.androidide.services.BuildService +import com.itsaky.androidide.tooling.api.IProject import com.itsaky.androidide.tooling.api.model.IdeAndroidModule -import com.itsaky.androidide.tooling.api.model.IdeGradleProject import com.itsaky.androidide.tooling.api.model.IdeJavaModule import com.itsaky.androidide.tooling.api.model.IdeModule +import com.itsaky.androidide.utils.ILogger import com.itsaky.lsp.java.models.JavaServerConfiguration import java.io.File import java.nio.file.Path +import java.util.concurrent.* /** * Manages projects in AndroidIDE. * * @author Akash Yadav */ +@Suppress("MemberVisibilityCanBePrivate") object ProjectManager { - private val log = com.itsaky.androidide.utils.ILogger.newInstance(javaClass.simpleName) - var rootProject: IdeGradleProject? = null + private val log = ILogger.newInstance(javaClass.simpleName) + var rootProject: IProject? = null + set(value) { + field = if (value == null) null else CachingProject(value) + + // Cache the module data in advance for future use + if (field != null) { + field!!.listModules() + } + } var projectPath: String? = null - fun getProjectDir(): File? = - if (getProjectDirPath() == null) null else File(getProjectDirPath()!!) + fun isInitialized() = this.rootProject != null && this.rootProject!!.isInitialized.get() + + fun checkInit(): Boolean { + if (isInitialized()) { + return true + } + + log.warn("Project is not initialized yet!") + return false + } + + fun getProjectDir(): File? = if (!checkInit()) null else File(getProjectDirPath()!!) fun getProjectDirPath(): String? = - if (rootProject == null) projectPath else rootProject!!.projectDir!!.absolutePath + if (!checkInit()) projectPath else rootProject!!.projectDir.get().absolutePath fun generateSources(builder: BuildService?) { if (builder == null) { @@ -51,62 +72,63 @@ object ProjectManager { return } - val app = - getApplicationModule() - ?: kotlin.run { - log.error("Cannot generate resources...") - return - } - - val debug = app.variants.firstOrNull { it.name == "debug" } - if (debug == null) { - log.warn("No debug variant found in application project ${app.name}") - return - } - - val mainArtifact = app.variants.first { it.name == "debug" }.mainArtifact - val genResourcesTask = mainArtifact.resGenTaskName - val genSourcesTask = mainArtifact.sourceGenTaskName - builder - .executeProjectTasks( - app.projectPath!!, - genResourcesTask ?: "", - genSourcesTask, - - // If view binding is enabled, generate the view binding classes too - if (app.viewBindingOptions != null && app.viewBindingOptions!!.isEnabled) - "dataBindingGenBaseClassesDebug" - else "") - .whenComplete { result, err -> - if (!result.isSuccessful || err != null) { - log.warn( - "Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.", - err ?: "") - return@whenComplete - } - - notifyProjectUpdate() + getApplicationModule().whenComplete { app, _ -> + if (app == null) { + log.warn( + "Cannot run resource and source generation task. No application module found.") + return@whenComplete } + + val debug = app.variants.firstOrNull { it.name == "debug" } + if (debug == null) { + log.warn("No debug variant found in application project ${app.name}") + return@whenComplete + } + + val mainArtifact = app.variants.first { it.name == "debug" }.mainArtifact + val genResourcesTask = mainArtifact.resGenTaskName + val genSourcesTask = mainArtifact.sourceGenTaskName + builder + .executeProjectTasks( + app.projectPath, + genResourcesTask ?: "", + genSourcesTask, + + // If view binding is enabled, generate the view binding classes too + if (app.viewBindingOptions != null && app.viewBindingOptions!!.isEnabled) + "dataBindingGenBaseClassesDebug" + else "") + .whenComplete { result, taskErr -> + if (!result.isSuccessful || taskErr != null) { + log.warn( + "Execution for tasks '$genResourcesTask' and '$genSourcesTask' failed.", + taskErr ?: "") + } else { + notifyProjectUpdate() + } + } + } } - fun getApplicationModule(): IdeAndroidModule? { + fun getApplicationModule(): CompletableFuture { if (rootProject == null) { log.error( "No root project instance is set. Is the project initialization process finished?") - return null + return CompletableFuture.completedFuture(null) } - val app = rootProject!!.findFirstAndroidModule() - if (app == null) { - log.error("No application module found in root project.") - return null + return rootProject!!.findFirstAndroidAppModule().whenComplete { app, err -> + if (app == null || err != null) { + log.debug("An error occurred while fetching model for application module.") + return@whenComplete + } } - - return app } - fun getApplicationResDirectories(): List? { - return collectResDirectories(getApplicationModule() ?: return null) + fun getApplicationResDirectories(): CompletableFuture?> { + return getApplicationModule().thenApplyAsync { + if (it == null) null else collectResDirectories(it) + } } fun collectResDirectories(android: IdeAndroidModule): List { @@ -133,15 +155,21 @@ object ProjectManager { } fun notifyProjectUpdate() { - val server = StudioApp.getInstance().javaLanguageServer - val sourceDirs = collectApplicationSourceDirs() - val classPaths = collectApplicationClassPaths() - val configuration = JavaServerConfiguration(classPaths, sourceDirs) - server.configurationChanged(configuration) + getApplicationModule().thenAccept { + if (it == null) { + log.debug("Cannot find application module. Unable to update class paths to LSP.") + return@thenAccept + } + + val server = StudioApp.getInstance().javaLanguageServer + val sourceDirs = collectSourceDirs(it) + val classPaths = collectClassPaths(it) + val configuration = JavaServerConfiguration(classPaths, sourceDirs) + server.configurationChanged(configuration) + } } - private fun collectApplicationClassPaths(): Set { - val app = getApplicationModule() ?: return emptySet() + private fun collectClassPaths(app: IdeAndroidModule): Set { val libraries = app.variantDependencies["debug"]!!.libraries val paths = mutableSetOf() @@ -153,7 +181,7 @@ object ProjectManager { paths.add(value.artifact!!.toPath()) } else { val project = - rootProject!!.findByPath(value.projectInfo!!.projectPath)!! as IdeModule + rootProject!!.findByPath(value.projectInfo!!.projectPath).get() as IdeModule paths.add(project.getGeneratedJar("debug").toPath()) if (project is IdeAndroidModule && project.projectPath != app.projectPath) { @@ -171,8 +199,7 @@ object ProjectManager { return paths } - private fun collectApplicationSourceDirs(): Set { - val app = getApplicationModule() ?: return emptySet() + private fun collectSourceDirs(app: IdeAndroidModule): Set { val sources = app.mainSourceSet ?: return emptySet() val javaDirs = sources.sourceProvider.javaDirectories val sourcePaths = javaDirs.toMutableSet() @@ -181,15 +208,12 @@ object ProjectManager { return sourcePaths.map { it.toPath() }.toSet() } - fun collectProjectDependencies( - project: IdeGradleProject, - app: IdeAndroidModule - ): List { + fun collectProjectDependencies(project: IProject, app: IdeAndroidModule): List { return app.variantDependencies["debug"]!! .libraries.values .filter { it.type == PROJECT } - .mapNotNull { project.findByPath(it.projectInfo!!.projectPath) } + .map { project.findByPath(it.projectInfo!!.projectPath) } .filterIsInstance(IdeModule::class.java) } @@ -245,38 +269,4 @@ object ProjectManager { android.variants.first { it.name == "debug" }.mainArtifact.generatedSourceFolders) return sources } - - /** - * Finds the module in which the given file exists. - * - * @return The module in which the given file exists or `null` if the module cannot be found. - */ - fun findModuleForFile(file: File): IdeModule? { - if (rootProject == null) { - log.warn( - "No root project instance is set. Is the project initialization process finished?") - return null - } - - val path = file.canonicalPath - val modules = rootProject!!.getModules() - - var longestPath = "" - var longestPathModule: IdeModule? = null - for (module in modules) { - val modulePath = module.projectDir!!.canonicalPath - if (path.startsWith(modulePath)) { - if (longestPath.length < modulePath.length && module is IdeModule) { - longestPathModule = module - longestPath = modulePath - } - } - } - - if (longestPath.isEmpty()) { - return null - } - - return longestPathModule - } } diff --git a/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java b/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java index eff5919d6..9f6965e92 100644 --- a/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java +++ b/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java @@ -54,6 +54,7 @@ import com.itsaky.androidide.tooling.api.messages.result.BuildResult; import com.itsaky.androidide.tooling.api.messages.result.GradleWrapperCheckResult; import com.itsaky.androidide.tooling.api.messages.result.InitializeResult; import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult; +import com.itsaky.androidide.tooling.api.IProject; import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher; import com.itsaky.androidide.tooling.events.ProgressEvent; import com.itsaky.androidide.utils.Environment; @@ -486,14 +487,16 @@ public class GradleBuildService extends Service implements BuildService, IToolin Environment.TOOLING_API_JAR.getAbsolutePath()); final var launcher = - ToolingApiLauncher.createClientLauncher( + ToolingApiLauncher.newClientLauncher( GradleBuildService.this, serverStreams.in, serverStreams.out); final var future = launcher.startListening(); GradleBuildService.this.startServerOutputReader(serverStreams.err); - GradleBuildService.this.server = launcher.getRemoteProxy(); + GradleBuildService.this.server = (IToolingApiServer) launcher.getRemoteProxy(); GradleBuildService.this.isToolingServerStarted = true; + ProjectManager.INSTANCE.setRootProject((IProject) launcher.getRemoteProxy()); + if (listener != null) { listener.onServerStarted(); } @@ -505,7 +508,8 @@ public class GradleBuildService extends Service implements BuildService, IToolin if (!(err instanceof CancellationException) && !(err instanceof InterruptedException)) { LOG.error( - "An error occurred while waiting for tooling API server to terminate", err); + "An error occurred while waiting for tooling API server to terminate", + err); } } diff --git a/build-tools/builder-model-impl/.gitignore b/build-tools/builder-model-impl/.gitignore new file mode 100644 index 000000000..42afabfd2 --- /dev/null +++ b/build-tools/builder-model-impl/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/build-tools/builder-model-impl/build.gradle b/build-tools/builder-model-impl/build.gradle new file mode 100644 index 000000000..b1466b94c --- /dev/null +++ b/build-tools/builder-model-impl/build.gradle @@ -0,0 +1,31 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +plugins { + id 'java-library' + id 'org.jetbrains.kotlin.jvm' +} + +java { + sourceCompatibility = rootProject.ext.javaSourceVersion + targetCompatibility = rootProject.ext.javaTargetVersion +} + +dependencies { + implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" + api "com.android.tools.build:builder-model:${project.property ("androidide.tooling.supportedAgpVersion")}" +} \ No newline at end of file diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidArtifact.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidArtifact.kt similarity index 97% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidArtifact.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidArtifact.kt index b2e678c1c..fa8e250ed 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidArtifact.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidArtifact.kt @@ -14,7 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.AndroidArtifact import com.android.builder.model.v2.ide.CodeShrinker diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidGradlePluginProjectFlags.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidGradlePluginProjectFlags.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidGradlePluginProjectFlags.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidGradlePluginProjectFlags.kt index 432323b35..b6dc65fc8 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidGradlePluginProjectFlags.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidGradlePluginProjectFlags.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.AndroidGradlePluginProjectFlags import com.android.builder.model.v2.ide.AndroidGradlePluginProjectFlags.BooleanFlag diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidLibraryData.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidLibraryData.kt similarity index 96% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidLibraryData.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidLibraryData.kt index 30c73a7e5..14431aca8 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultAndroidLibraryData.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultAndroidLibraryData.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.AndroidLibraryData import java.io.File diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultApiVersion.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultApiVersion.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultApiVersion.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultApiVersion.kt index efb511379..687ed4f5a 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultApiVersion.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultApiVersion.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.ApiVersion import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultArtifactDependencies.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultArtifactDependencies.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultArtifactDependencies.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultArtifactDependencies.kt index ce0ec0143..8be32f7eb 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultArtifactDependencies.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultArtifactDependencies.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.ArtifactDependencies import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultBundleInfo.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultBundleInfo.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultBundleInfo.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultBundleInfo.kt index 5b4582e73..d9c4f5f9e 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultBundleInfo.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultBundleInfo.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.BundleInfo import java.io.File diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultCustomSourceDirectory.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultCustomSourceDirectory.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultCustomSourceDirectory.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultCustomSourceDirectory.kt index 478a65fb4..bb3e04845 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultCustomSourceDirectory.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultCustomSourceDirectory.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.CustomSourceDirectory import java.io.File diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultGraphItem.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultGraphItem.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultGraphItem.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultGraphItem.kt index b972a6239..6168c857a 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultGraphItem.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultGraphItem.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.GraphItem import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultJavaArtifact.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultJavaArtifact.kt similarity index 96% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultJavaArtifact.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultJavaArtifact.kt index 65a688557..5cb5b9a05 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultJavaArtifact.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultJavaArtifact.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.JavaArtifact import java.io.File diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultJavaCompileOptions.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultJavaCompileOptions.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultJavaCompileOptions.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultJavaCompileOptions.kt index af4d86f05..900d8c603 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultJavaCompileOptions.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultJavaCompileOptions.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.JavaCompileOptions import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultLibrary.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultLibrary.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultLibrary.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultLibrary.kt index 906cf2630..d40306ed0 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultLibrary.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultLibrary.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.Library import com.android.builder.model.v2.ide.LibraryType diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultLibraryInfo.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultLibraryInfo.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultLibraryInfo.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultLibraryInfo.kt index 70f6efb1e..7676c45fb 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultLibraryInfo.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultLibraryInfo.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.LibraryInfo import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultModelBuilderParameter.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultModelBuilderParameter.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultModelBuilderParameter.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultModelBuilderParameter.kt index 08b35ef52..a60685d19 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultModelBuilderParameter.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultModelBuilderParameter.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.models.ModelBuilderParameter import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultModelSyncFile.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultModelSyncFile.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultModelSyncFile.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultModelSyncFile.kt index ed5b87882..ff088a62b 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultModelSyncFile.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultModelSyncFile.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ModelSyncFile import com.android.builder.model.v2.ModelSyncFile.ModelSyncType diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultProjectInfo.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultProjectInfo.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultProjectInfo.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultProjectInfo.kt index 4f58772f4..d6a4b8c1e 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultProjectInfo.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultProjectInfo.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.ProjectInfo import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultProjectSyncIssues.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultProjectSyncIssues.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultProjectSyncIssues.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultProjectSyncIssues.kt index 4e6a06baa..5a5659cd3 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultProjectSyncIssues.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultProjectSyncIssues.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.models.ProjectSyncIssues import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSourceProvider.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSourceProvider.kt similarity index 96% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSourceProvider.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSourceProvider.kt index 98c34148d..f41b131dc 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSourceProvider.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSourceProvider.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.SourceProvider import java.io.File diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSourceSetContainer.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSourceSetContainer.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSourceSetContainer.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSourceSetContainer.kt index ab3653ba6..4e587d5c8 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSourceSetContainer.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSourceSetContainer.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.SourceSetContainer import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSyncIssue.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSyncIssue.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSyncIssue.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSyncIssue.kt index f494f27ee..e14820e54 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultSyncIssue.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultSyncIssue.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.SyncIssue import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultTestInfo.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultTestInfo.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultTestInfo.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultTestInfo.kt index 785446034..a531b87e9 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultTestInfo.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultTestInfo.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.TestInfo import com.android.builder.model.v2.ide.TestInfo.Execution diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultTestedTargetVariant.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultTestedTargetVariant.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultTestedTargetVariant.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultTestedTargetVariant.kt index ecd0a1d3a..faaa8e995 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultTestedTargetVariant.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultTestedTargetVariant.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.TestedTargetVariant import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultUnresolvedDependency.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultUnresolvedDependency.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultUnresolvedDependency.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultUnresolvedDependency.kt index 7c1a946c3..9bc67e8bd 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultUnresolvedDependency.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultUnresolvedDependency.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.UnresolvedDependency import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultVariant.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultVariant.kt similarity index 96% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultVariant.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultVariant.kt index fc082d0c1..af1c70b4d 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultVariant.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultVariant.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.Variant import java.io.File diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultVariantDependencies.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultVariantDependencies.kt similarity index 95% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultVariantDependencies.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultVariantDependencies.kt index 8784b7d55..9b68d6ccf 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultVariantDependencies.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultVariantDependencies.kt @@ -14,8 +14,7 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.models.VariantDependencies import java.io.Serializable diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultViewBindingOptions.kt b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultViewBindingOptions.kt similarity index 94% rename from build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultViewBindingOptions.kt rename to build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultViewBindingOptions.kt index ed218ac8b..1d3f52fcd 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/internal/DefaultViewBindingOptions.kt +++ b/build-tools/builder-model-impl/src/main/java/com/itsaky/androidide/builder/model/DefaultViewBindingOptions.kt @@ -15,7 +15,7 @@ * along with AndroidIDE. If not, see . */ -package com.itsaky.androidide.tooling.api.model.internal +package com.itsaky.androidide.builder.model import com.android.builder.model.v2.ide.ViewBindingOptions import java.io.Serializable diff --git a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java index 9330e310a..66097c4c0 100644 --- a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java +++ b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/Main.java @@ -21,7 +21,6 @@ import static com.itsaky.androidide.utils.ILogger.newInstance; import com.itsaky.androidide.models.LogLine; import com.itsaky.androidide.tooling.api.IToolingApiClient; -import com.itsaky.androidide.tooling.api.model.IProject; import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher; import com.itsaky.androidide.tooling.impl.model.InternalForwardingProject; import com.itsaky.androidide.tooling.impl.progress.ForwardingProgressListener; diff --git a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt index 605d831d5..9325aa9d6 100644 --- a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt +++ b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt @@ -37,7 +37,7 @@ import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult.Fai import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult.Failure.UNSUPPORTED_CONFIGURATION import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult.Failure.UNSUPPORTED_GRADLE_VERSION import com.itsaky.androidide.tooling.api.model.IdeGradleProject -import com.itsaky.androidide.tooling.api.model.internal.DefaultProjectSyncIssues +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues import com.itsaky.androidide.tooling.impl.model.InternalForwardingProject import com.itsaky.androidide.tooling.impl.progress.ForwardingProgressListener import com.itsaky.androidide.tooling.impl.util.ProjectReader @@ -229,6 +229,7 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa this.client = null this.project = null this.buildCancellationToken = null + this.forwardingProject.project = null Main.future = null Main.client = null diff --git a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/model/InternalForwardingProject.kt b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/model/InternalForwardingProject.kt index 9a4c6d93d..a259e1542 100644 --- a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/model/InternalForwardingProject.kt +++ b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/model/InternalForwardingProject.kt @@ -17,8 +17,9 @@ package com.itsaky.androidide.tooling.impl.model +import com.itsaky.androidide.tooling.api.IProject import com.itsaky.androidide.tooling.api.IToolingApiClient -import com.itsaky.androidide.tooling.api.model.IProject +import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData import com.itsaky.androidide.tooling.api.model.IdeAndroidModule import com.itsaky.androidide.tooling.api.model.IdeGradleProject import com.itsaky.androidide.tooling.api.model.IdeGradleTask @@ -40,6 +41,10 @@ class InternalForwardingProject(var project: IProject?) : IProject { private val log = ILogger.newInstance(javaClass.simpleName) + override fun isInitialized(): CompletableFuture { + return CompletableFuture.completedFuture(this.project != null) + } + override fun getName(): CompletableFuture = if (this.project != null) this.project!!.name else CompletableFuture.completedFuture("") @@ -71,6 +76,10 @@ class InternalForwardingProject(var project: IProject?) : IProject { if (this.project != null) this.project!!.modules else CompletableFuture.completedFuture(mutableListOf()) + override fun listModules(): CompletableFuture> = + if (this.project != null) project!!.listModules() + else CompletableFuture.completedFuture(mutableListOf()) + override fun findByPath(path: String): CompletableFuture = if (this.project != null) this.project!!.findByPath(path) else CompletableFuture.completedFuture(null) @@ -82,4 +91,8 @@ class InternalForwardingProject(var project: IProject?) : IProject { override fun findFirstAndroidModule(): CompletableFuture = if (this.project != null) this.project!!.findFirstAndroidModule() else CompletableFuture.completedFuture(null) + + override fun findFirstAndroidAppModule(): CompletableFuture = + if (this.project != null) this.project!!.findFirstAndroidAppModule() + else CompletableFuture.completedFuture(null) } diff --git a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ModelInfoContainer.kt b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ModelInfoContainer.kt index 29c87a1cd..0268124bc 100644 --- a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ModelInfoContainer.kt +++ b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ModelInfoContainer.kt @@ -18,7 +18,7 @@ package com.itsaky.androidide.tooling.impl.util import com.itsaky.androidide.tooling.api.model.IdeAndroidModule -import com.itsaky.androidide.tooling.api.model.internal.DefaultProjectSyncIssues +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues import java.io.Serializable /** @author Akash Yadav */ diff --git a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java index def052872..de7062272 100644 --- a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java +++ b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/util/ProjectReader.java @@ -36,7 +36,7 @@ import com.itsaky.androidide.tooling.api.model.JavaModuleDependency; import com.itsaky.androidide.tooling.api.model.JavaModuleExternalDependency; import com.itsaky.androidide.tooling.api.model.JavaModuleProjectDependency; import com.itsaky.androidide.tooling.api.model.JavaSourceDirectory; -import com.itsaky.androidide.tooling.api.model.internal.DefaultProjectSyncIssues; +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues; import com.itsaky.androidide.tooling.api.model.util.AndroidModulePropertyCopier; import com.itsaky.androidide.tooling.api.model.util.ProjectBuilder; import com.itsaky.androidide.tooling.impl.Main; diff --git a/build-tools/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/ToolingApiImplTest.kt b/build-tools/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/ToolingApiImplTest.kt index 96cf7f5a5..a50a02ecd 100644 --- a/build-tools/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/ToolingApiImplTest.kt +++ b/build-tools/tooling-api-impl/src/test/java/com/itsaky/androidide/tooling/impl/ToolingApiImplTest.kt @@ -27,7 +27,7 @@ import com.itsaky.androidide.tooling.api.IToolingApiServer import com.itsaky.androidide.tooling.api.messages.InitializeProjectMessage import com.itsaky.androidide.tooling.api.messages.result.BuildResult import com.itsaky.androidide.tooling.api.messages.result.GradleWrapperCheckResult -import com.itsaky.androidide.tooling.api.model.IProject +import com.itsaky.androidide.tooling.api.IProject import com.itsaky.androidide.tooling.api.model.IdeAndroidModule import com.itsaky.androidide.tooling.api.model.IdeJavaModule import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher diff --git a/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar b/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar index 0a5f4759c..c056714b4 100644 Binary files a/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar and b/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar differ diff --git a/build-tools/tooling-api-model/build.gradle b/build-tools/tooling-api-model/build.gradle index 2e59871c7..8ab540808 100644 --- a/build-tools/tooling-api-model/build.gradle +++ b/build-tools/tooling-api-model/build.gradle @@ -14,7 +14,6 @@ * You should have received a copy of the GNU General Public License * along with AndroidIDE. If not, see . */ - plugins { id 'java-library' id 'org.jetbrains.kotlin.jvm' @@ -34,9 +33,9 @@ repositories { dependencies { implementation project (path: ':build-tools:logger') implementation project (path: ':build-tools:xml-dom') + api project (path: ':build-tools:builder-model-impl') implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - api "com.android.tools.build:builder-model:${project.property ("androidide.tooling.supportedAgpVersion")}" } task copyToTestDir (type: Copy) { diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.java b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.java new file mode 100644 index 000000000..f30fb870f --- /dev/null +++ b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/IProject.java @@ -0,0 +1,98 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.tooling.api; + +import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData; +import com.itsaky.androidide.tooling.api.model.IdeAndroidModule; +import com.itsaky.androidide.tooling.api.model.IdeGradleProject; +import com.itsaky.androidide.tooling.api.model.IdeGradleTask; + +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; +import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; +import org.jetbrains.annotations.NotNull; + +import java.io.File; +import java.util.List; +import java.util.concurrent.CompletableFuture; + +/** + * Root Gradle project. The getter methods return completed {@link CompletableFuture}s. + * + * @author Akash Yadav + */ +@JsonSegment("project") +public interface IProject { + + @NotNull + @JsonRequest + default CompletableFuture isInitialized() { + return CompletableFuture.completedFuture(true); + } + + @NotNull + @JsonRequest + CompletableFuture getName(); + + @NotNull + @JsonRequest + CompletableFuture getDescription(); + + @NotNull + @JsonRequest + CompletableFuture getProjectPath(); + + @NotNull + @JsonRequest + CompletableFuture getProjectDir(); + + @NotNull + @JsonRequest + CompletableFuture getBuildDir(); + + @NotNull + @JsonRequest + CompletableFuture getBuildScript(); + + @NotNull + @JsonRequest + CompletableFuture> getTasks(); + + @NotNull + @JsonRequest + CompletableFuture> getModules(); + + @NotNull + @JsonRequest + CompletableFuture> listModules(); + + @NotNull + @JsonRequest + CompletableFuture findByPath(@NotNull String path); + + @NotNull + @JsonRequest + CompletableFuture findFirstAndroidModule(); + + @NotNull + @JsonRequest + CompletableFuture findFirstAndroidAppModule(); + + @NotNull + @JsonRequest + CompletableFuture> findAndroidModules(); +} diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleModuleData.kt b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleModuleData.kt new file mode 100644 index 000000000..6dc9d4299 --- /dev/null +++ b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/messages/result/SimpleModuleData.kt @@ -0,0 +1,27 @@ +/* + * This file is part of AndroidIDE. + * + * AndroidIDE is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * AndroidIDE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with AndroidIDE. If not, see . + */ + +package com.itsaky.androidide.tooling.api.messages.result + +import java.io.File + +/** + * Container for basic data about module projects. + * + * @author Akash Yadav + */ +data class SimpleModuleData(val name: String, val path: String, val projectDir: File) diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IProject.java b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IProject.java deleted file mode 100644 index 74dbf04d6..000000000 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IProject.java +++ /dev/null @@ -1,79 +0,0 @@ -/* - * This file is part of AndroidIDE. - * - * AndroidIDE is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * AndroidIDE is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with AndroidIDE. If not, see . - */ - -package com.itsaky.androidide.tooling.api.model; - -import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; -import org.eclipse.lsp4j.jsonrpc.services.JsonSegment; -import org.jetbrains.annotations.NotNull; - -import java.io.File; -import java.util.List; -import java.util.concurrent.CompletableFuture; - -/** - * Root Gradle project. The getter methods return completed {@link CompletableFuture}s. - * - * @author Akash Yadav - */ -@JsonSegment("project") -public interface IProject { - - @NotNull - @JsonRequest - CompletableFuture getName(); - - @NotNull - @JsonRequest - CompletableFuture getDescription(); - - @NotNull - @JsonRequest - CompletableFuture getProjectPath(); - - @NotNull - @JsonRequest - CompletableFuture getProjectDir(); - - @NotNull - @JsonRequest - CompletableFuture getBuildDir(); - - @NotNull - @JsonRequest - CompletableFuture getBuildScript(); - - @NotNull - @JsonRequest - CompletableFuture> getTasks(); - - @NotNull - @JsonRequest - CompletableFuture> getModules(); - - @NotNull - @JsonRequest - CompletableFuture findByPath(@NotNull String path); - - @NotNull - @JsonRequest - CompletableFuture> findAndroidModules(); - - @NotNull - @JsonRequest - CompletableFuture findFirstAndroidModule(); -} diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeAndroidModule.kt b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeAndroidModule.kt index dfcf10ee0..4ff48f267 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeAndroidModule.kt +++ b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeAndroidModule.kt @@ -18,14 +18,14 @@ package com.itsaky.androidide.tooling.api.model import com.android.builder.model.v2.ide.ProjectType import com.android.builder.model.v2.models.AndroidProject -import com.itsaky.androidide.tooling.api.model.internal.DefaultAndroidGradlePluginProjectFlags -import com.itsaky.androidide.tooling.api.model.internal.DefaultJavaCompileOptions -import com.itsaky.androidide.tooling.api.model.internal.DefaultModelSyncFile -import com.itsaky.androidide.tooling.api.model.internal.DefaultSourceProvider -import com.itsaky.androidide.tooling.api.model.internal.DefaultSourceSetContainer -import com.itsaky.androidide.tooling.api.model.internal.DefaultVariant -import com.itsaky.androidide.tooling.api.model.internal.DefaultVariantDependencies -import com.itsaky.androidide.tooling.api.model.internal.DefaultViewBindingOptions +import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags +import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions +import com.itsaky.androidide.builder.model.DefaultModelSyncFile +import com.itsaky.androidide.builder.model.DefaultSourceProvider +import com.itsaky.androidide.builder.model.DefaultSourceSetContainer +import com.itsaky.androidide.builder.model.DefaultVariant +import com.itsaky.androidide.builder.model.DefaultVariantDependencies +import com.itsaky.androidide.builder.model.DefaultViewBindingOptions import org.eclipse.lemminx.dom.DOMParser import org.eclipse.lemminx.uriresolver.URIResolverExtensionManager import java.io.File diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeGradleProject.kt b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeGradleProject.kt index 6e583c87f..b96386371 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeGradleProject.kt +++ b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/IdeGradleProject.kt @@ -16,10 +16,12 @@ */ package com.itsaky.androidide.tooling.api.model +import com.android.builder.model.v2.ide.ProjectType.APPLICATION +import com.itsaky.androidide.tooling.api.messages.result.SimpleModuleData +import org.eclipse.lsp4j.jsonrpc.CompletableFutures import java.io.File import java.io.Serializable import java.util.concurrent.* -import org.eclipse.lsp4j.jsonrpc.CompletableFutures /** * A root Gradle project. @@ -34,7 +36,7 @@ open class IdeGradleProject( @JvmField val buildScript: File, @JvmField val parent: IdeGradleProject?, @JvmField val tasks: List, -) : IProject, Serializable { +) : com.itsaky.androidide.tooling.api.IProject, Serializable { private val serialVersionUID = 1L private val gsonType: String = javaClass.name @@ -72,6 +74,14 @@ open class IdeGradleProject( return CompletableFutures.computeAsync { this.moduleProjects.toMutableList() } } + override fun listModules(): CompletableFuture> { + return CompletableFutures.computeAsync { + return@computeAsync this.moduleProjects + .map { SimpleModuleData(it.name, it.projectPath, it.projectDir) } + .toMutableList() + } + } + override fun findByPath(path: String): CompletableFuture { return CompletableFutures.computeAsync { if (path == this.projectPath) { @@ -91,4 +101,12 @@ open class IdeGradleProject( override fun findFirstAndroidModule(): CompletableFuture { return findAndroidModules().thenApply { it.firstOrNull() } } + + override fun findFirstAndroidAppModule(): CompletableFuture { + return findAndroidModules().thenApply { modules -> + val application = + modules.stream().filter { it != null && it.projectType == APPLICATION }.findFirst() + application.orElse(null) + } + } } diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/AndroidModulePropertyCopier.kt b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/AndroidModulePropertyCopier.kt index 7b089c211..8c2ff504a 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/AndroidModulePropertyCopier.kt +++ b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/AndroidModulePropertyCopier.kt @@ -43,30 +43,30 @@ import com.android.builder.model.v2.ide.ViewBindingOptions import com.android.builder.model.v2.models.ProjectSyncIssues import com.android.builder.model.v2.models.VariantDependencies import com.itsaky.androidide.tooling.api.model.IdeAndroidModule -import com.itsaky.androidide.tooling.api.model.internal.DefaultAndroidArtifact -import com.itsaky.androidide.tooling.api.model.internal.DefaultAndroidGradlePluginProjectFlags -import com.itsaky.androidide.tooling.api.model.internal.DefaultAndroidLibraryData -import com.itsaky.androidide.tooling.api.model.internal.DefaultApiVersion -import com.itsaky.androidide.tooling.api.model.internal.DefaultArtifactDependencies -import com.itsaky.androidide.tooling.api.model.internal.DefaultBundleInfo -import com.itsaky.androidide.tooling.api.model.internal.DefaultCustomSourceDirectory -import com.itsaky.androidide.tooling.api.model.internal.DefaultGraphItem -import com.itsaky.androidide.tooling.api.model.internal.DefaultJavaArtifact -import com.itsaky.androidide.tooling.api.model.internal.DefaultJavaCompileOptions -import com.itsaky.androidide.tooling.api.model.internal.DefaultLibrary -import com.itsaky.androidide.tooling.api.model.internal.DefaultLibraryInfo -import com.itsaky.androidide.tooling.api.model.internal.DefaultModelSyncFile -import com.itsaky.androidide.tooling.api.model.internal.DefaultProjectInfo -import com.itsaky.androidide.tooling.api.model.internal.DefaultProjectSyncIssues -import com.itsaky.androidide.tooling.api.model.internal.DefaultSourceProvider -import com.itsaky.androidide.tooling.api.model.internal.DefaultSourceSetContainer -import com.itsaky.androidide.tooling.api.model.internal.DefaultSyncIssue -import com.itsaky.androidide.tooling.api.model.internal.DefaultTestInfo -import com.itsaky.androidide.tooling.api.model.internal.DefaultTestedTargetVariant -import com.itsaky.androidide.tooling.api.model.internal.DefaultUnresolvedDependency -import com.itsaky.androidide.tooling.api.model.internal.DefaultVariant -import com.itsaky.androidide.tooling.api.model.internal.DefaultVariantDependencies -import com.itsaky.androidide.tooling.api.model.internal.DefaultViewBindingOptions +import com.itsaky.androidide.builder.model.DefaultAndroidArtifact +import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags +import com.itsaky.androidide.builder.model.DefaultAndroidLibraryData +import com.itsaky.androidide.builder.model.DefaultApiVersion +import com.itsaky.androidide.builder.model.DefaultArtifactDependencies +import com.itsaky.androidide.builder.model.DefaultBundleInfo +import com.itsaky.androidide.builder.model.DefaultCustomSourceDirectory +import com.itsaky.androidide.builder.model.DefaultGraphItem +import com.itsaky.androidide.builder.model.DefaultJavaArtifact +import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions +import com.itsaky.androidide.builder.model.DefaultLibrary +import com.itsaky.androidide.builder.model.DefaultLibraryInfo +import com.itsaky.androidide.builder.model.DefaultModelSyncFile +import com.itsaky.androidide.builder.model.DefaultProjectInfo +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues +import com.itsaky.androidide.builder.model.DefaultSourceProvider +import com.itsaky.androidide.builder.model.DefaultSourceSetContainer +import com.itsaky.androidide.builder.model.DefaultSyncIssue +import com.itsaky.androidide.builder.model.DefaultTestInfo +import com.itsaky.androidide.builder.model.DefaultTestedTargetVariant +import com.itsaky.androidide.builder.model.DefaultUnresolvedDependency +import com.itsaky.androidide.builder.model.DefaultVariant +import com.itsaky.androidide.builder.model.DefaultVariantDependencies +import com.itsaky.androidide.builder.model.DefaultViewBindingOptions import com.itsaky.androidide.utils.ILogger /** diff --git a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/ProjectBuilder.kt b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/ProjectBuilder.kt index bc8113332..85a5e5f9f 100644 --- a/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/ProjectBuilder.kt +++ b/build-tools/tooling-api-model/src/main/java/com/itsaky/androidide/tooling/api/model/util/ProjectBuilder.kt @@ -23,11 +23,11 @@ import com.itsaky.androidide.tooling.api.model.IdeGradleTask import com.itsaky.androidide.tooling.api.model.IdeJavaModule import com.itsaky.androidide.tooling.api.model.JavaContentRoot import com.itsaky.androidide.tooling.api.model.JavaModuleDependency -import com.itsaky.androidide.tooling.api.model.internal.DefaultAndroidGradlePluginProjectFlags -import com.itsaky.androidide.tooling.api.model.internal.DefaultJavaCompileOptions -import com.itsaky.androidide.tooling.api.model.internal.DefaultModelSyncFile -import com.itsaky.androidide.tooling.api.model.internal.DefaultVariant -import com.itsaky.androidide.tooling.api.model.internal.DefaultViewBindingOptions +import com.itsaky.androidide.builder.model.DefaultAndroidGradlePluginProjectFlags +import com.itsaky.androidide.builder.model.DefaultJavaCompileOptions +import com.itsaky.androidide.builder.model.DefaultModelSyncFile +import com.itsaky.androidide.builder.model.DefaultVariant +import com.itsaky.androidide.builder.model.DefaultViewBindingOptions import java.io.File /** diff --git a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/result/InitializeResult.kt b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/result/InitializeResult.kt index e62c763c5..f9bf57eab 100644 --- a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/result/InitializeResult.kt +++ b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/result/InitializeResult.kt @@ -18,7 +18,7 @@ package com.itsaky.androidide.tooling.api.messages.result import com.itsaky.androidide.tooling.api.model.IdeGradleProject -import com.itsaky.androidide.tooling.api.model.internal.DefaultProjectSyncIssues +import com.itsaky.androidide.builder.model.DefaultProjectSyncIssues /** * Result received after an initialize project request. diff --git a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.java b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.java index 875a85dfd..a7eda1bd0 100644 --- a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.java +++ b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/util/ToolingApiLauncher.java @@ -20,7 +20,7 @@ package com.itsaky.androidide.tooling.api.util; import com.google.gson.GsonBuilder; import com.itsaky.androidide.tooling.api.IToolingApiClient; import com.itsaky.androidide.tooling.api.IToolingApiServer; -import com.itsaky.androidide.tooling.api.model.IProject; +import com.itsaky.androidide.tooling.api.IProject; import com.itsaky.androidide.tooling.api.model.IdeAndroidModule; import com.itsaky.androidide.tooling.api.model.IdeGradleProject; import com.itsaky.androidide.tooling.api.model.IdeGradleTask; @@ -86,10 +86,6 @@ import java.util.concurrent.Executors; * @author Akash Yadav */ public class ToolingApiLauncher { - public static Launcher createClientLauncher( - IToolingApiClient client, InputStream in, OutputStream out) { - return createIOLauncher(client, IToolingApiServer.class, in, out); - } public static Launcher createIOLauncher( Object local, Class remote, InputStream in, OutputStream out) { @@ -265,9 +261,4 @@ public class ToolingApiLauncher { return newIoLauncher( new Object[] {server, project}, new Class[] {IToolingApiClient.class}, in, out); } - - public static Launcher createServerLauncher( - IToolingApiServer server, InputStream in, OutputStream out) { - return createIOLauncher(server, IToolingApiClient.class, in, out); - } } diff --git a/settings.gradle b/settings.gradle index 00ce7150e..8df61a0cc 100755 --- a/settings.gradle +++ b/settings.gradle @@ -27,3 +27,4 @@ include ':build-tools:tooling-api-model' include ':build-tools:xml-dom' include ':build-tools:xml-formatter' include ':build-tools:fuzzysearch' +include ':build-tools:builder-model-impl'