mirror of
https://gitee.com/zhangsken/AndroidIDE.git
synced 2026-10-16 07:49:44 +08:00
Removed unneccessary legacy project and task models
This commit is contained in:
@@ -99,8 +99,6 @@ import com.itsaky.androidide.models.LogLine;
|
||||
import com.itsaky.androidide.models.SaveResult;
|
||||
import com.itsaky.androidide.models.SearchResult;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
import com.itsaky.androidide.projects.AndroidProject;
|
||||
import com.itsaky.androidide.projects.IDEProject;
|
||||
import com.itsaky.androidide.projects.ProjectManager;
|
||||
import com.itsaky.androidide.services.GradleBuildService;
|
||||
import com.itsaky.androidide.services.LogReceiver;
|
||||
@@ -159,7 +157,7 @@ public class EditorActivity extends StudioActivity
|
||||
EditorActivityProvider,
|
||||
OptionsListFragment.OnOptionsClickListener {
|
||||
|
||||
public static final String EXTRA_PROJECT = "project";
|
||||
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;
|
||||
@@ -283,34 +281,6 @@ public class EditorActivity extends StudioActivity
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidProject provideAndroidProject() {
|
||||
return getAndroidProject();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDEProject provideIDEProject() {
|
||||
return getIDEProject();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private IDEProject getIDEProject() {
|
||||
if (mViewModel == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mViewModel.getIDEProject();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private AndroidProject getAndroidProject() {
|
||||
if (mViewModel == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return mViewModel.getAndroidProject();
|
||||
}
|
||||
|
||||
public void handleSearchResults(Map<File, List<SearchResult>> results) {
|
||||
setSearchResultAdapter(
|
||||
new com.itsaky.androidide.adapters.SearchListAdapter(
|
||||
@@ -344,7 +314,7 @@ public class EditorActivity extends StudioActivity
|
||||
shell.bgAppend(
|
||||
String.format(
|
||||
"cd '%s' && sh gradlew --status",
|
||||
Objects.requireNonNull(getAndroidProject()).getProjectPath()));
|
||||
Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDir())));
|
||||
if (!getDaemonStatusFragment().isShowing()) {
|
||||
getDaemonStatusFragment().show(getSupportFragmentManager(), "daemon_status");
|
||||
}
|
||||
@@ -808,7 +778,8 @@ public class EditorActivity extends StudioActivity
|
||||
intent.putExtra(
|
||||
DesignerActivity.KEY_LAYOUT_PATH,
|
||||
getCurrentEditor().getFile().getAbsolutePath());
|
||||
intent.putStringArrayListExtra(DesignerActivity.KEY_RES_DIRS, getResourceDirectories());
|
||||
intent.putStringArrayListExtra(
|
||||
DesignerActivity.KEY_RES_DIRS, (ArrayList<String>) getResourceDirectories());
|
||||
LOG.info("Launching UI Designer...");
|
||||
mUIDesignerLauncher.launch(intent);
|
||||
} catch (Throwable th) {
|
||||
@@ -822,21 +793,13 @@ public class EditorActivity extends StudioActivity
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private ArrayList<String> getResourceDirectories() {
|
||||
final var dirs = new ArrayList<String>();
|
||||
if (getAndroidProject() != null
|
||||
&& getAndroidProject().getModulePaths() != null
|
||||
&& !getAndroidProject().getModulePaths().isEmpty()) {
|
||||
for (String path : getAndroidProject().getModulePaths()) {
|
||||
if (path != null && new File(path).exists()) {
|
||||
File res = new File(path, "src/main/res");
|
||||
if (res.exists()) {
|
||||
dirs.add(res.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
private List<String> getResourceDirectories() {
|
||||
final var dirs = ProjectManager.INSTANCE.getApplicationResDirectories();
|
||||
if (dirs == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return dirs;
|
||||
|
||||
return dirs.stream().map(File::getAbsolutePath).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean saveAll(boolean notify, boolean canProcessResources) {
|
||||
@@ -1049,10 +1012,6 @@ public class EditorActivity extends StudioActivity
|
||||
execTasks(null, "clean", "build");
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
////////////// PRIVATE APIS /////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
public AlertDialog getFindInProjectDialog() {
|
||||
return mFindInProjectDialog == null ? createFindInProjectDialog() : mFindInProjectDialog;
|
||||
}
|
||||
@@ -1064,6 +1023,10 @@ public class EditorActivity extends StudioActivity
|
||||
}
|
||||
}
|
||||
|
||||
/////////////////////////////////////////////////
|
||||
////////////// PRIVATE APIS /////////////////////
|
||||
/////////////////////////////////////////////////
|
||||
|
||||
public GradleBuildService getBuildService() {
|
||||
return mBuildService;
|
||||
}
|
||||
@@ -1076,7 +1039,7 @@ public class EditorActivity extends StudioActivity
|
||||
mViewModel = new ViewModelProvider(this).get(EditorViewModel.class);
|
||||
getProjectFromIntent();
|
||||
|
||||
mFileTreeFragment = FileTreeFragment.newInstance(this.getAndroidProject());
|
||||
mFileTreeFragment = FileTreeFragment.newInstance();
|
||||
mDaemonStatusFragment = new TextSheetFragment().setTextSelectable(true);
|
||||
|
||||
setupDrawerToggle();
|
||||
@@ -1260,13 +1223,13 @@ public class EditorActivity extends StudioActivity
|
||||
private void initializeProject() {
|
||||
// TODO Do not create unnecessary models. Instead, just keep a reference to the root
|
||||
// directory of the project.
|
||||
final var androidProject = getAndroidProject();
|
||||
if (androidProject == null) {
|
||||
final var projectPath = ProjectManager.INSTANCE.getProjectDir();
|
||||
if (projectPath == null) {
|
||||
LOG.error("Cannot initialize project. Project model is null.");
|
||||
return;
|
||||
}
|
||||
|
||||
final var projectDir = new File(androidProject.getProjectPath());
|
||||
final var projectDir = new File(projectPath);
|
||||
if (!projectDir.exists()) {
|
||||
LOG.error("Project directory does not exist. Cannot initialize project");
|
||||
return;
|
||||
@@ -1462,25 +1425,21 @@ public class EditorActivity extends StudioActivity
|
||||
final Intent intent = new Intent(this, TerminalActivity.class);
|
||||
intent.putExtra(
|
||||
TerminalActivity.KEY_WORKING_DIRECTORY,
|
||||
Objects.requireNonNull(getAndroidProject()).getProjectPath());
|
||||
Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDir()));
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void getProjectFromIntent() {
|
||||
final var project = (AndroidProject) getIntent().getParcelableExtra(EXTRA_PROJECT);
|
||||
if (mViewModel != null) {
|
||||
mViewModel.setAndroidProject(project);
|
||||
} else {
|
||||
LOG.error("ViewModel is null. Cannot set project.");
|
||||
}
|
||||
final var project = getIntent().getStringExtra(EXTRA_PROJECT_PATH);
|
||||
ProjectManager.INSTANCE.setProjectPath(project);
|
||||
|
||||
getApp().getPrefManager()
|
||||
.setOpenedProject(
|
||||
Objects.requireNonNull(this.getAndroidProject()).getProjectPath());
|
||||
.setOpenedProject(Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDir()));
|
||||
|
||||
try {
|
||||
//noinspection ConstantConditions
|
||||
getSupportActionBar()
|
||||
.setSubtitle(new File(getAndroidProject().getProjectPath()).getName());
|
||||
.setSubtitle(new File(ProjectManager.INSTANCE.getProjectDir()).getName());
|
||||
} catch (Throwable th) {
|
||||
// ignored
|
||||
}
|
||||
@@ -1536,7 +1495,7 @@ public class EditorActivity extends StudioActivity
|
||||
final var javaLanguageServer = getApp().getJavaLanguageServer();
|
||||
final var workspaceRoots = new HashSet<Path>();
|
||||
workspaceRoots.add(
|
||||
new File(Objects.requireNonNull(getAndroidProject()).getProjectPath()).toPath());
|
||||
new File(Objects.requireNonNull(ProjectManager.INSTANCE.getProjectDir())).toPath());
|
||||
workspaceRoots.add(Environment.HOME.toPath().resolve("logsender"));
|
||||
|
||||
final var params = new InitializeParams(workspaceRoots);
|
||||
@@ -1607,28 +1566,35 @@ public class EditorActivity extends StudioActivity
|
||||
builder.show();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NonNull
|
||||
private AlertDialog createFindInProjectDialog() {
|
||||
if (getAndroidProject() == null
|
||||
|| getAndroidProject().getModulePaths() == null
|
||||
|| getAndroidProject().getModulePaths().size() <= 0) {
|
||||
getApp().toast(R.string.msg_no_modules, Toaster.Type.ERROR);
|
||||
return null;
|
||||
List<File> moduleDirs = null;
|
||||
final var rootProject = ProjectManager.INSTANCE.getRootProject();
|
||||
if (rootProject == null) {
|
||||
LOG.warn("No root project model found. Is the project initialized?");
|
||||
moduleDirs = new ArrayList<>(0);
|
||||
}
|
||||
final List<String> modules = getAndroidProject().getModulePaths();
|
||||
|
||||
if (moduleDirs == null) {
|
||||
moduleDirs =
|
||||
rootProject.getModules().stream()
|
||||
.map(IdeGradleProject::getProjectDir)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
final List<File> srcDirs = new ArrayList<>();
|
||||
final LayoutSearchProjectBinding binding =
|
||||
LayoutSearchProjectBinding.inflate(getLayoutInflater());
|
||||
binding.modulesContainer.removeAllViews();
|
||||
for (int i = 0; i < modules.size(); i++) {
|
||||
final File file = new File(modules.get(i));
|
||||
final File src = new File(file, "src");
|
||||
if (!file.exists() || !file.isDirectory() || !src.exists() || !src.isDirectory()) {
|
||||
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(file.getName());
|
||||
check.setText(module.getName());
|
||||
check.setChecked(true);
|
||||
|
||||
LinearLayout.MarginLayoutParams params = new LinearLayout.MarginLayoutParams(-2, -2);
|
||||
|
||||
@@ -39,18 +39,15 @@ import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.models.NewProjectDetails;
|
||||
import com.itsaky.androidide.models.ProjectTemplate;
|
||||
import com.itsaky.androidide.projects.AndroidProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.ProjectCreatorCallable;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.ProjectFinder;
|
||||
import com.itsaky.androidide.utils.TransformUtils;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
import abhishekti7.unicorn.filepicker.UnicornFilePicker;
|
||||
|
||||
@@ -68,11 +65,213 @@ public class MainActivity extends StudioActivity
|
||||
private int currentTemplateIndex = 0;
|
||||
|
||||
@Override
|
||||
protected View bindLayout() {
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
createBinding = binding.createLayout;
|
||||
createLayoutBinding = createBinding.createProjectContent;
|
||||
return binding.getRoot();
|
||||
public void onClick(View p1) {
|
||||
if (p1.getId() == binding.createProject.getId()) {
|
||||
showCreateProject();
|
||||
} else if (p1.getId() == createBinding.createprojectClose.getId()) {
|
||||
hideCreateProject();
|
||||
} else if (p1.getId() == createLayoutBinding.nextCard.getId()) {
|
||||
currentTemplateIndex++;
|
||||
if (currentTemplateIndex >= mTemplates.size()) {
|
||||
currentTemplateIndex = 0;
|
||||
}
|
||||
showTemplate(currentTemplateIndex);
|
||||
} else if (p1.getId() == createLayoutBinding.previousCard.getId()) {
|
||||
currentTemplateIndex--;
|
||||
if (currentTemplateIndex < 0) {
|
||||
currentTemplateIndex = mTemplates.size() - 1;
|
||||
}
|
||||
showTemplate(currentTemplateIndex);
|
||||
} else if (p1.getId() == createLayoutBinding.createprojectCreate.getId()) {
|
||||
createNewProject();
|
||||
} else if (p1.getId() == binding.gotoPreferences.getId()) {
|
||||
gotoSettings();
|
||||
} else if (p1.getId() == binding.openProject.getId()) {
|
||||
pickProject();
|
||||
} else if (p1.getId() == binding.openTerminal.getId()) {
|
||||
startActivity(new Intent(this, TerminalActivity.class));
|
||||
}
|
||||
}
|
||||
|
||||
private void showCreateProject() {
|
||||
MaterialContainerTransform transform =
|
||||
TransformUtils.createContainerTransformFor(
|
||||
binding.createProject, binding.createNewCard, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
|
||||
binding.createNewCard.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void hideCreateProject() {
|
||||
MaterialContainerTransform transform =
|
||||
TransformUtils.createContainerTransformFor(
|
||||
binding.createNewCard, binding.createProject, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
|
||||
binding.createNewCard.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private void showTemplate(int index) {
|
||||
if (index < 0 || index >= mTemplates.size()) {
|
||||
return;
|
||||
}
|
||||
final ProjectTemplate template = mTemplates.get(index);
|
||||
createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
|
||||
createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
|
||||
createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
|
||||
}
|
||||
|
||||
private void createNewProject() {
|
||||
final String appName =
|
||||
Objects.requireNonNull(createLayoutBinding.createprojectTextAppName.getEditText())
|
||||
.getText()
|
||||
.toString()
|
||||
.trim();
|
||||
final String packageName =
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextPackageName.getEditText())
|
||||
.getText()
|
||||
.toString()
|
||||
.trim();
|
||||
final int minSdk = getMinSdk();
|
||||
final int targetSdk = getTargetSdk();
|
||||
|
||||
if (!isValid(appName, packageName, minSdk, targetSdk)) {
|
||||
getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
createProject(appName, packageName, minSdk, targetSdk);
|
||||
}
|
||||
|
||||
private void gotoSettings() {
|
||||
startActivity(new Intent(this, PreferencesActivity.class));
|
||||
}
|
||||
|
||||
private void pickProject() {
|
||||
UnicornFilePicker.from(this)
|
||||
.addConfigBuilder()
|
||||
.addItemDivider(false)
|
||||
.selectMultipleFiles(false)
|
||||
.setRootDirectory(Environment.getExternalStorageDirectory().getAbsolutePath())
|
||||
.showHiddenFiles(true)
|
||||
.showOnlyDirectory(true)
|
||||
.theme(R.style.AppTheme_FilePicker)
|
||||
.build()
|
||||
.forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
|
||||
}
|
||||
|
||||
private int getMinSdk() {
|
||||
try {
|
||||
return Integer.parseInt(
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextMinSdk.getEditText())
|
||||
.getText()
|
||||
.toString());
|
||||
} catch (Exception e) {
|
||||
getApp().toast(e.getMessage(), Toaster.Type.ERROR);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int getTargetSdk() {
|
||||
try {
|
||||
return Integer.parseInt(
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextTargetSdk.getEditText())
|
||||
.getText()
|
||||
.toString());
|
||||
} catch (Exception e) {
|
||||
getApp().toast(e.getMessage(), Toaster.Type.ERROR);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private boolean isValid(String appName, String packageName, int minSdk, int targetSdk) {
|
||||
return appName != null
|
||||
&& appName.length() > 0
|
||||
&& packageName != null
|
||||
&& packageName.length() > 0
|
||||
&& minSdk > 1
|
||||
&& minSdk < 99
|
||||
&& targetSdk > 1
|
||||
&& targetSdk < 99;
|
||||
}
|
||||
|
||||
private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
new ProjectCreatorCallable(
|
||||
mTemplates.get(currentTemplateIndex),
|
||||
new NewProjectDetails(appName, packageName, minSdk, targetSdk),
|
||||
this),
|
||||
r -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeBegin() {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
setMessage(R.string.msg_begin_project_write);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProcessTask(String taskName) {
|
||||
setMessage(taskName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(File root) {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
if (mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
|
||||
|
||||
openProject(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(String reason) {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
if (mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
getApp().toast(reason, Toaster.Type.ERROR);
|
||||
}
|
||||
|
||||
private void openProject(File root) {
|
||||
final var intent = new Intent(this, EditorActivity.class);
|
||||
intent.putExtra(EditorActivity.EXTRA_PROJECT_PATH, root.getAbsolutePath());
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void createProgressSheet() {
|
||||
mProgressSheet = new ProgressSheet();
|
||||
mProgressSheet.setShowShadow(false);
|
||||
}
|
||||
|
||||
private void setMessage(int msg) {
|
||||
setMessage(getString(msg));
|
||||
}
|
||||
|
||||
private void setMessage(String msg) {
|
||||
mProgressSheet.setMessage(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (binding.createNewCard.getVisibility() == View.VISIBLE) {
|
||||
hideCreateProject();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -127,20 +326,6 @@ public class MainActivity extends StudioActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void askProjectOpenPermission(File root) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||
builder.setTitle(R.string.title_confirm_open_project);
|
||||
builder.setMessage(getString(R.string.msg_confirm_open_project, root.getAbsolutePath()));
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.yes, (d, w) -> openProject(root));
|
||||
builder.setNegativeButton(R.string.no, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void gotoSettings() {
|
||||
startActivity(new Intent(this, PreferencesActivity.class));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onStorageGranted() {}
|
||||
|
||||
@@ -151,120 +336,11 @@ public class MainActivity extends StudioActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View p1) {
|
||||
if (p1.getId() == binding.createProject.getId()) {
|
||||
showCreateProject();
|
||||
} else if (p1.getId() == createBinding.createprojectClose.getId()) {
|
||||
hideCreateProject();
|
||||
} else if (p1.getId() == createLayoutBinding.nextCard.getId()) {
|
||||
currentTemplateIndex++;
|
||||
if (currentTemplateIndex >= mTemplates.size()) {
|
||||
currentTemplateIndex = 0;
|
||||
}
|
||||
showTemplate(currentTemplateIndex);
|
||||
} else if (p1.getId() == createLayoutBinding.previousCard.getId()) {
|
||||
currentTemplateIndex--;
|
||||
if (currentTemplateIndex < 0) {
|
||||
currentTemplateIndex = mTemplates.size() - 1;
|
||||
}
|
||||
showTemplate(currentTemplateIndex);
|
||||
} else if (p1.getId() == createLayoutBinding.createprojectCreate.getId()) {
|
||||
createNewProject();
|
||||
} else if (p1.getId() == binding.gotoPreferences.getId()) {
|
||||
gotoSettings();
|
||||
} else if (p1.getId() == binding.openProject.getId()) {
|
||||
pickProject();
|
||||
} else if (p1.getId() == binding.openTerminal.getId()) {
|
||||
startActivity(new Intent(this, TerminalActivity.class));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeBegin() {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
setMessage(R.string.msg_begin_project_write);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProcessTask(String taskName) {
|
||||
setMessage(taskName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(File root) {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
if (mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
getApp().toast(R.string.project_created_successfully, Toaster.Type.SUCCESS);
|
||||
|
||||
openProject(root);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailed(String reason) {
|
||||
if (mProgressSheet == null) {
|
||||
createProgressSheet();
|
||||
}
|
||||
|
||||
if (mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
getApp().toast(reason, Toaster.Type.ERROR);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE
|
||||
&& resultCode == RESULT_OK) {
|
||||
ArrayList<String> files = data.getStringArrayListExtra("filePaths");
|
||||
if (files != null) {
|
||||
if (files.size() == 1) {
|
||||
File choseDir = new File(files.get(0));
|
||||
if (choseDir.exists() && choseDir.isDirectory()) {
|
||||
openProject(choseDir);
|
||||
} else {
|
||||
getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
|
||||
}
|
||||
} else {
|
||||
getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (binding.createNewCard.getVisibility() == View.VISIBLE) {
|
||||
hideCreateProject();
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (mProgressSheet != null && mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
}
|
||||
|
||||
private void showTemplate(int index) {
|
||||
if (index < 0 || index >= mTemplates.size()) {
|
||||
return;
|
||||
}
|
||||
final ProjectTemplate template = mTemplates.get(index);
|
||||
createLayoutBinding.createprojectTemplateLabel.setText(template.getName());
|
||||
createLayoutBinding.createprojectTemplateDescription.setText(template.getDescription());
|
||||
createLayoutBinding.createprojectTemplateImage.setImageResource(template.getImageId());
|
||||
protected View bindLayout() {
|
||||
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
||||
createBinding = binding.createLayout;
|
||||
createLayoutBinding = createBinding.createProjectContent;
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
/** Must be called in/after onViewCreated */
|
||||
@@ -310,147 +386,42 @@ public class MainActivity extends StudioActivity
|
||||
mTemplates.add(libgdx);
|
||||
}
|
||||
|
||||
private void createProgressSheet() {
|
||||
mProgressSheet = new ProgressSheet();
|
||||
mProgressSheet.setShowShadow(false);
|
||||
private void askProjectOpenPermission(File root) {
|
||||
final MaterialAlertDialogBuilder builder = DialogUtils.newMaterialDialogBuilder(this);
|
||||
builder.setTitle(R.string.title_confirm_open_project);
|
||||
builder.setMessage(getString(R.string.msg_confirm_open_project, root.getAbsolutePath()));
|
||||
builder.setCancelable(false);
|
||||
builder.setPositiveButton(R.string.yes, (d, w) -> openProject(root));
|
||||
builder.setNegativeButton(R.string.no, null);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void setMessage(int msg) {
|
||||
setMessage(getString(msg));
|
||||
}
|
||||
|
||||
private void setMessage(String msg) {
|
||||
mProgressSheet.setMessage(msg);
|
||||
}
|
||||
|
||||
private void openProject(File root) {
|
||||
setMessage(R.string.msg_opening_project);
|
||||
mProgressSheet.setCancelable(false);
|
||||
mProgressSheet.show(getSupportFragmentManager(), "opening_project");
|
||||
|
||||
new TaskExecutor().executeAsync(new ProjectReader(root), this::openEditor);
|
||||
}
|
||||
|
||||
private void pickProject() {
|
||||
UnicornFilePicker.from(this)
|
||||
.addConfigBuilder()
|
||||
.addItemDivider(false)
|
||||
.selectMultipleFiles(false)
|
||||
.setRootDirectory(Environment.getExternalStorageDirectory().getAbsolutePath())
|
||||
.showHiddenFiles(true)
|
||||
.showOnlyDirectory(true)
|
||||
.theme(R.style.AppTheme_FilePicker)
|
||||
.build()
|
||||
.forResult(abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE);
|
||||
}
|
||||
|
||||
private void openEditor(AndroidProject project) {
|
||||
if (project == null) {
|
||||
getApp().toast(R.string.msg_invalid_project, Toaster.Type.ERROR);
|
||||
} else {
|
||||
startActivity(
|
||||
new Intent(this, EditorActivity.class)
|
||||
.putExtra(EditorActivity.EXTRA_PROJECT, project));
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode == abhishekti7.unicorn.filepicker.utils.Constants.REQ_UNICORN_FILE
|
||||
&& resultCode == RESULT_OK) {
|
||||
ArrayList<String> files = data.getStringArrayListExtra("filePaths");
|
||||
if (files != null) {
|
||||
if (files.size() == 1) {
|
||||
File choseDir = new File(files.get(0));
|
||||
if (choseDir.exists() && choseDir.isDirectory()) {
|
||||
openProject(choseDir);
|
||||
} else {
|
||||
getApp().toast(R.string.msg_picked_isnt_dir, Toaster.Type.ERROR);
|
||||
}
|
||||
} else {
|
||||
getApp().toast(R.string.msg_pick_single_file, Toaster.Type.ERROR);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createNewProject() {
|
||||
final String appName =
|
||||
Objects.requireNonNull(createLayoutBinding.createprojectTextAppName.getEditText())
|
||||
.getText()
|
||||
.toString()
|
||||
.trim();
|
||||
final String packageName =
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextPackageName.getEditText())
|
||||
.getText()
|
||||
.toString()
|
||||
.trim();
|
||||
final int minSdk = getMinSdk();
|
||||
final int targetSdk = getTargetSdk();
|
||||
|
||||
if (!isValid(appName, packageName, minSdk, targetSdk)) {
|
||||
getApp().toast(R.string.invalid_values, Toaster.Type.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
createProject(appName, packageName, minSdk, targetSdk);
|
||||
}
|
||||
|
||||
private void createProject(String appName, String packageName, int minSdk, int targetSdk) {
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
new ProjectCreatorCallable(
|
||||
mTemplates.get(currentTemplateIndex),
|
||||
new NewProjectDetails(appName, packageName, minSdk, targetSdk),
|
||||
this),
|
||||
r -> {});
|
||||
}
|
||||
|
||||
private boolean isValid(String appName, String packageName, int minSdk, int targetSdk) {
|
||||
return appName != null
|
||||
&& appName.length() > 0
|
||||
&& packageName != null
|
||||
&& packageName.length() > 0
|
||||
&& minSdk > 1
|
||||
&& minSdk < 99
|
||||
&& targetSdk > 1
|
||||
&& targetSdk < 99;
|
||||
}
|
||||
|
||||
private int getMinSdk() {
|
||||
try {
|
||||
return Integer.parseInt(
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextMinSdk.getEditText())
|
||||
.getText()
|
||||
.toString());
|
||||
} catch (Exception e) {
|
||||
getApp().toast(e.getMessage(), Toaster.Type.ERROR);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int getTargetSdk() {
|
||||
try {
|
||||
return Integer.parseInt(
|
||||
Objects.requireNonNull(
|
||||
createLayoutBinding.createprojectTextTargetSdk.getEditText())
|
||||
.getText()
|
||||
.toString());
|
||||
} catch (Exception e) {
|
||||
getApp().toast(e.getMessage(), Toaster.Type.ERROR);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void showCreateProject() {
|
||||
MaterialContainerTransform transform =
|
||||
TransformUtils.createContainerTransformFor(
|
||||
binding.createProject, binding.createNewCard, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
|
||||
binding.createNewCard.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
private void hideCreateProject() {
|
||||
MaterialContainerTransform transform =
|
||||
TransformUtils.createContainerTransformFor(
|
||||
binding.createNewCard, binding.createProject, binding.realContainer);
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), transform);
|
||||
binding.createNewCard.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
private static final class ProjectReader implements Callable<AndroidProject> {
|
||||
|
||||
private final File root;
|
||||
|
||||
public ProjectReader(File root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidProject call() throws Exception {
|
||||
return ProjectFinder.fromFolder(root);
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
if (mProgressSheet != null && mProgressSheet.isShowing()) {
|
||||
mProgressSheet.dismiss();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
package com.itsaky.androidide.actions.editor
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.itsaky.androidide.R
|
||||
import com.itsaky.androidide.actions.ActionData
|
||||
import com.itsaky.androidide.actions.BaseEditorAction
|
||||
|
||||
@@ -1,171 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.adapters;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.transition.ChangeBounds;
|
||||
import androidx.transition.TransitionManager;
|
||||
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.bumptech.glide.Glide;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutProjectsItemBinding;
|
||||
import com.itsaky.androidide.projects.AndroidProject;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
|
||||
public class ProjectsListAdapter extends RecyclerView.Adapter<ProjectsListAdapter.VH> {
|
||||
private ArrayList<AndroidProject> mProjects;
|
||||
private OnProjectClickListener listener;
|
||||
|
||||
private final Comparator<AndroidProject> PROJECTS_SORTER =
|
||||
new Comparator<AndroidProject>() {
|
||||
|
||||
@Override
|
||||
public int compare(AndroidProject p1, AndroidProject p2) {
|
||||
return p1.getAppName().compareTo(p2.getAppName());
|
||||
}
|
||||
};
|
||||
|
||||
public ProjectsListAdapter(
|
||||
ArrayList<AndroidProject> mProjects, OnProjectClickListener listener) {
|
||||
this.mProjects = mProjects;
|
||||
this.listener = listener;
|
||||
Collections.sort(this.mProjects, PROJECTS_SORTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ProjectsListAdapter.VH onCreateViewHolder(ViewGroup p1, int p2) {
|
||||
return new VH(
|
||||
LayoutProjectsItemBinding.inflate(LayoutInflater.from(p1.getContext()), p1, false));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ProjectsListAdapter.VH holder, int p2) {
|
||||
final AndroidProject project = mProjects.get(p2);
|
||||
holder.binding.projecsitemName.setText(project.getAppName());
|
||||
holder.binding.projecsitemPkgName.setText(project.getPackageName());
|
||||
holder.binding.deleteMsg.setText(
|
||||
holder.binding
|
||||
.deleteMsg
|
||||
.getContext()
|
||||
.getString(R.string.msg_confirm_delete, project.getAppName()));
|
||||
if (!TextUtils.isEmpty(project.getIconPath()))
|
||||
Glide.with(holder.itemView.getContext().getApplicationContext())
|
||||
.load(new File(project.getIconPath()))
|
||||
.into(holder.binding.projecsitemIcon);
|
||||
else holder.binding.projecsitemIcon.setImageResource(R.drawable.ic_android);
|
||||
|
||||
holder.binding.projectsitemClickableContainer.setOnClickListener(
|
||||
v -> sendClickEvent(project));
|
||||
holder.binding.projectsitemClickableContainer.setOnLongClickListener(
|
||||
v -> holder.toggleDelete());
|
||||
holder.binding.delete.setOnClickListener(v -> holder.showConfirmDelete());
|
||||
holder.binding.deleteCancel.setOnClickListener(v -> holder.hideAllDelete());
|
||||
holder.binding.deleteOk.setOnClickListener(v -> deleteProject(project, p2));
|
||||
}
|
||||
|
||||
private void deleteProject(AndroidProject project, int index) {
|
||||
index = index >= mProjects.size() ? mProjects.size() - 1 : index;
|
||||
if (index < 0) return;
|
||||
mProjects.remove(index);
|
||||
notifyItemRemoved(index);
|
||||
|
||||
new TaskExecutor()
|
||||
.executeAsync(
|
||||
() -> FileUtils.delete(project.getProjectPath()),
|
||||
__ -> {
|
||||
StudioApp.getInstance()
|
||||
.toast(
|
||||
__
|
||||
? R.string.msg_project_deleted
|
||||
: R.string.msg_project_delete_failed,
|
||||
Toaster.Type.SUCCESS);
|
||||
});
|
||||
}
|
||||
|
||||
private void sendClickEvent(AndroidProject project) {
|
||||
if (listener != null) listener.onProjectClick(project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return mProjects.size();
|
||||
}
|
||||
|
||||
public class VH extends RecyclerView.ViewHolder {
|
||||
LayoutProjectsItemBinding binding;
|
||||
|
||||
public VH(LayoutProjectsItemBinding binding) {
|
||||
super(binding.getRoot());
|
||||
this.binding = binding;
|
||||
}
|
||||
|
||||
public void showConfirmDelete() {
|
||||
binding.confirmDeleteContainer.setEnabled(true);
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.confirmDeleteContainer.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void hideAllDelete() {
|
||||
hideDelete();
|
||||
hideConfirmDelete();
|
||||
}
|
||||
|
||||
public boolean toggleDelete() {
|
||||
binding.delete.setEnabled(!binding.delete.isEnabled());
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.delete.setVisibility(binding.delete.isEnabled() ? View.VISIBLE : View.GONE);
|
||||
return true;
|
||||
}
|
||||
|
||||
public void hideDelete() {
|
||||
binding.delete.setEnabled(false);
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.delete.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
public void hideConfirmDelete() {
|
||||
binding.confirmDeleteContainer.setEnabled(false);
|
||||
TransitionManager.beginDelayedTransition(
|
||||
binding.getRoot(), new ChangeBounds().setDuration(85));
|
||||
binding.confirmDeleteContainer.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
public interface OnProjectClickListener {
|
||||
public void onProjectClick(AndroidProject project);
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.adapters.viewholders.FileTreeViewHolder;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.databinding.LayoutEditorFileTreeBinding;
|
||||
import com.itsaky.androidide.projects.AndroidProject;
|
||||
import com.itsaky.androidide.projects.ProjectManager;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.tasks.callables.FileTreeCallable;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
@@ -63,19 +63,27 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
private LayoutEditorFileTreeBinding binding;
|
||||
private AndroidTreeView mFileTreeView;
|
||||
private FileActionListener mFileActionListener;
|
||||
private AndroidProject mProject;
|
||||
private TreeNode mRoot;
|
||||
private String mTreeState;
|
||||
|
||||
public FileTreeFragment() {}
|
||||
|
||||
@NonNull
|
||||
public static FileTreeFragment newInstance(AndroidProject project) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putParcelable("project", project);
|
||||
FileTreeFragment frag = new FileTreeFragment();
|
||||
frag.setArguments(bundle);
|
||||
return frag;
|
||||
public static FileTreeFragment newInstance() {
|
||||
return new FileTreeFragment();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
mFileActionListener = (FileActionListener) context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
saveTreeState();
|
||||
outState.putString(KEY_STORED_TREE_STATE, mTreeState);
|
||||
}
|
||||
|
||||
public void saveTreeState() {
|
||||
@@ -87,49 +95,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
}
|
||||
}
|
||||
|
||||
private void tryRestoreState() {
|
||||
tryRestoreState(mTreeState);
|
||||
}
|
||||
|
||||
private void tryRestoreState(String state) {
|
||||
if (!TextUtils.isEmpty(state) && mFileTreeView != null) {
|
||||
|
||||
LOG.debug("Restoring tree view state:", "'" + state + "'");
|
||||
|
||||
mFileTreeView.collapseAll();
|
||||
final var openNodesArray = state.split(NODES_PATH_SEPARATOR);
|
||||
final var openNodes = new HashSet<>(Arrays.asList(openNodesArray));
|
||||
restoreNodeState(mRoot, openNodes);
|
||||
} else {
|
||||
LOG.error(
|
||||
"Unable to restore tree state",
|
||||
"treeState=" + state,
|
||||
"treeView=" + mFileTreeView);
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreNodeState(@NonNull TreeNode root, Set<String> openNodes) {
|
||||
final var children = root.getChildren();
|
||||
for (int i = 0, childrenSize = children.size(); i < childrenSize; i++) {
|
||||
final var node = children.get(i);
|
||||
if (openNodes.contains(node.getPath())) {
|
||||
listNode(
|
||||
node,
|
||||
() -> {
|
||||
updateChevron(node);
|
||||
expandNode(node);
|
||||
restoreNodeState(node, openNodes);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAttach(@NonNull Context context) {
|
||||
super.onAttach(context);
|
||||
mFileActionListener = (FileActionListener) context;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(
|
||||
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
@@ -140,7 +105,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
mProject = requireArguments().getParcelable("project");
|
||||
|
||||
if (savedInstanceState != null && savedInstanceState.containsKey(KEY_STORED_TREE_STATE)) {
|
||||
mTreeState = savedInstanceState.getString(KEY_STORED_TREE_STATE, null);
|
||||
@@ -188,11 +152,28 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaveInstanceState(@NonNull Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
saveTreeState();
|
||||
outState.putString(KEY_STORED_TREE_STATE, mTreeState);
|
||||
public void collapseNode(TreeNode node) {
|
||||
if (mFileTreeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
|
||||
mFileTreeView.collapseNode(node);
|
||||
}
|
||||
|
||||
public void expandNode(TreeNode node) {
|
||||
if (mFileTreeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
|
||||
mFileTreeView.expandNode(node);
|
||||
}
|
||||
|
||||
private void setLoading(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).setLoading();
|
||||
}
|
||||
}
|
||||
|
||||
private void listNode(@NonNull TreeNode node, Runnable whenDone) {
|
||||
@@ -222,6 +203,12 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
__ -> finalWhenDone.run());
|
||||
}
|
||||
|
||||
private void updateChevron(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded());
|
||||
}
|
||||
}
|
||||
|
||||
private void getNodeFromFiles(File[] files, TreeNode parent) {
|
||||
Arrays.sort(files, new FileTreeCallable.SortFileName());
|
||||
Arrays.sort(files, new FileTreeCallable.SortFolder());
|
||||
@@ -232,18 +219,6 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
}
|
||||
}
|
||||
|
||||
private void setLoading(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).setLoading();
|
||||
}
|
||||
}
|
||||
|
||||
private void updateChevron(@NonNull TreeNode node) {
|
||||
if (node.getViewHolder() instanceof FileTreeViewHolder) {
|
||||
((FileTreeViewHolder) node.getViewHolder()).updateChevron(!node.isExpanded());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onLongClick(TreeNode node, Object value) {
|
||||
if (mFileActionListener != null) {
|
||||
@@ -252,32 +227,15 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
return true;
|
||||
}
|
||||
|
||||
public void expandNode(TreeNode node) {
|
||||
if (mFileTreeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
|
||||
mFileTreeView.expandNode(node);
|
||||
}
|
||||
|
||||
public void collapseNode(TreeNode node) {
|
||||
if (mFileTreeView == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionManager.beginDelayedTransition(binding.getRoot(), new ChangeBounds());
|
||||
mFileTreeView.collapseNode(node);
|
||||
}
|
||||
|
||||
public void listProjectFiles() {
|
||||
if (mProject == null) {
|
||||
final var projectDirPath = ProjectManager.INSTANCE.getProjectDir();
|
||||
if (projectDirPath == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final File gradleProps = Environment.GRADLE_PROPS;
|
||||
final File gradleHome = Environment.GRADLE_USER_HOME;
|
||||
File projectDir = new File(mProject.getProjectPath());
|
||||
File projectDir = new File(projectDirPath);
|
||||
mRoot = TreeNode.root(projectDir);
|
||||
if (gradleHome.exists() && gradleHome.isDirectory()) {
|
||||
if (!gradleProps.exists()) {
|
||||
@@ -342,6 +300,43 @@ public class FileTreeFragment extends BottomSheetDialogFragment
|
||||
return binding.fileTreeLoadingProgress;
|
||||
}
|
||||
|
||||
private void tryRestoreState() {
|
||||
tryRestoreState(mTreeState);
|
||||
}
|
||||
|
||||
private void tryRestoreState(String state) {
|
||||
if (!TextUtils.isEmpty(state) && mFileTreeView != null) {
|
||||
|
||||
LOG.debug("Restoring tree view state:", "'" + state + "'");
|
||||
|
||||
mFileTreeView.collapseAll();
|
||||
final var openNodesArray = state.split(NODES_PATH_SEPARATOR);
|
||||
final var openNodes = new HashSet<>(Arrays.asList(openNodesArray));
|
||||
restoreNodeState(mRoot, openNodes);
|
||||
} else {
|
||||
LOG.error(
|
||||
"Unable to restore tree state",
|
||||
"treeState=" + state,
|
||||
"treeView=" + mFileTreeView);
|
||||
}
|
||||
}
|
||||
|
||||
private void restoreNodeState(@NonNull TreeNode root, Set<String> openNodes) {
|
||||
final var children = root.getChildren();
|
||||
for (int i = 0, childrenSize = children.size(); i < childrenSize; i++) {
|
||||
final var node = children.get(i);
|
||||
if (openNodes.contains(node.getPath())) {
|
||||
listNode(
|
||||
node,
|
||||
() -> {
|
||||
updateChevron(node);
|
||||
expandNode(node);
|
||||
restoreNodeState(node, openNodes);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public interface FileActionListener {
|
||||
CodeEditorView openFile(File file);
|
||||
|
||||
|
||||
+1
-1
@@ -17,7 +17,6 @@
|
||||
*/
|
||||
package com.itsaky.androidide.fragments.preferences;
|
||||
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import static com.itsaky.androidide.managers.PreferenceManager.KEY_EDITOR_AUTO_SAVE;
|
||||
import static com.itsaky.androidide.managers.PreferenceManager.KEY_EDITOR_DRAW_HEX;
|
||||
import static com.itsaky.androidide.managers.PreferenceManager.KEY_EDITOR_FLAG_LINE_BREAK;
|
||||
@@ -44,6 +43,7 @@ import androidx.preference.SwitchPreference;
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.databinding.LayoutTextSizeSliderBinding;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.models.ConstantsBridge;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ import com.itsaky.androidide.databinding.LayoutCreateFileJavaBinding;
|
||||
import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding;
|
||||
import com.itsaky.androidide.fragments.sheets.OptionsListFragment;
|
||||
import com.itsaky.androidide.models.SheetOption;
|
||||
import com.itsaky.androidide.projects.ProjectManager;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.ProjectWriter;
|
||||
@@ -93,34 +94,27 @@ public class FileOptionsHandler extends IDEHandler
|
||||
if (forceUnknownType) {
|
||||
createNewEmptyFile(f);
|
||||
} else {
|
||||
final var projectDir = ProjectManager.INSTANCE.getProjectDir();
|
||||
Objects.requireNonNull(projectDir);
|
||||
|
||||
final boolean isJava =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ JAVA_PATH_REGEX)
|
||||
Pattern.compile(Pattern.quote(projectDir) + JAVA_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ RES_PATH_REGEX)
|
||||
Pattern.compile(Pattern.quote(projectDir) + RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isLayoutRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ LAYOUT_RES_PATH_REGEX)
|
||||
Pattern.compile(Pattern.quote(projectDir) + LAYOUT_RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isMenuRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ MENU_RES_PATH_REGEX)
|
||||
Pattern.compile(Pattern.quote(projectDir) + MENU_RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
final boolean isDrawableRes =
|
||||
Pattern.compile(
|
||||
Pattern.quote(androidProject().getProjectPath())
|
||||
+ DRAWABLE_RES_PATH_REGEX)
|
||||
Pattern.compile(Pattern.quote(projectDir) + DRAWABLE_RES_PATH_REGEX)
|
||||
.matcher(f.getAbsolutePath())
|
||||
.find();
|
||||
if (isJava) {
|
||||
|
||||
@@ -18,8 +18,6 @@
|
||||
package com.itsaky.androidide.handlers;
|
||||
|
||||
import com.itsaky.androidide.EditorActivity;
|
||||
import com.itsaky.androidide.projects.AndroidProject;
|
||||
import com.itsaky.androidide.projects.IDEProject;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
|
||||
/**
|
||||
@@ -36,22 +34,14 @@ public abstract class IDEHandler {
|
||||
this.provider = provider;
|
||||
}
|
||||
|
||||
protected EditorActivity activity() {
|
||||
return provider.provideEditorActivity();
|
||||
}
|
||||
|
||||
protected AndroidProject androidProject() {
|
||||
return provider.provideAndroidProject();
|
||||
}
|
||||
|
||||
protected IDEProject ideProject() {
|
||||
return provider.provideIDEProject();
|
||||
}
|
||||
|
||||
public abstract void start();
|
||||
|
||||
public abstract void stop();
|
||||
|
||||
protected EditorActivity activity() {
|
||||
return provider.provideEditorActivity();
|
||||
}
|
||||
|
||||
/** An interface to communicate between a handler and its client */
|
||||
public interface Provider {
|
||||
|
||||
@@ -61,19 +51,5 @@ public abstract class IDEHandler {
|
||||
* @throws NullPointerException is this is required
|
||||
*/
|
||||
EditorActivity provideEditorActivity();
|
||||
|
||||
/**
|
||||
* Called by handler to get a reference of the current {@link AndroidProject}
|
||||
*
|
||||
* @throws NullPointerException is this is required
|
||||
*/
|
||||
AndroidProject provideAndroidProject();
|
||||
|
||||
/**
|
||||
* Called by handler to get a reference of the current {@link IDEProject}
|
||||
*
|
||||
* @throws NullPointerException is this is required
|
||||
*/
|
||||
IDEProject provideIDEProject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
package com.itsaky.androidide.language.xml;
|
||||
|
||||
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.OPERATOR;
|
||||
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.TEXT_SELECTED;
|
||||
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.forComment;
|
||||
import static com.itsaky.androidide.syntax.colorschemes.SchemeAndroidIDE.get;
|
||||
import static io.github.rosemoe.sora.widget.schemes.EditorColorScheme.LITERAL;
|
||||
|
||||
@@ -25,8 +25,8 @@ import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.language.CommonCompletionProvider;
|
||||
import com.itsaky.androidide.language.IDELanguage;
|
||||
import com.itsaky.androidide.lexers.xml.XMLLexer;
|
||||
import com.itsaky.androidide.utils.JavaCharacter;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
import com.itsaky.androidide.utils.JavaCharacter;
|
||||
import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
import com.itsaky.lsp.api.ILanguageServer;
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ import com.itsaky.androidide.models.DiagnosticGroup;
|
||||
import com.itsaky.androidide.models.SearchResult;
|
||||
import com.itsaky.androidide.tasks.TaskExecutor;
|
||||
import com.itsaky.androidide.utils.DialogUtils;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
import com.itsaky.androidide.utils.LSPUtils;
|
||||
import com.itsaky.androidide.views.editor.CodeEditorView;
|
||||
import com.itsaky.androidide.views.editor.IDEEditor;
|
||||
import com.itsaky.lsp.api.ILanguageClient;
|
||||
|
||||
@@ -1,225 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.projects;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class AndroidProject implements Parcelable {
|
||||
|
||||
private String appName;
|
||||
private String packageName;
|
||||
private String iconPath;
|
||||
private String projectPath;
|
||||
private String mainModule;
|
||||
private String mainSourcePath;
|
||||
|
||||
private List<String> modulePaths = new ArrayList<>();
|
||||
private List<String> sourcePaths = new ArrayList<>();
|
||||
private List<String> classPaths = new ArrayList<>();
|
||||
|
||||
public AndroidProject() {
|
||||
addSource(StudioApp.getInstance().getLogSenderDir().getAbsolutePath());
|
||||
}
|
||||
|
||||
protected AndroidProject(Parcel in) {
|
||||
appName = in.readString();
|
||||
packageName = in.readString();
|
||||
iconPath = in.readString();
|
||||
projectPath = in.readString();
|
||||
mainModule = in.readString();
|
||||
mainSourcePath = in.readString();
|
||||
in.readList(modulePaths, String.class.getClassLoader());
|
||||
in.readList(sourcePaths, String.class.getClassLoader());
|
||||
in.readList(classPaths, String.class.getClassLoader());
|
||||
}
|
||||
|
||||
public File getBuildDir() {
|
||||
return new File(getMainModulePath(), "build");
|
||||
}
|
||||
|
||||
public AndroidProject setMainModule(String mainModule) {
|
||||
this.mainModule = mainModule;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMainModule() {
|
||||
return mainModule;
|
||||
}
|
||||
|
||||
public String getMainModulePath() {
|
||||
return new File(getProjectPath(), getMainModule()).getAbsolutePath();
|
||||
}
|
||||
|
||||
public AndroidProject setMainSourcePath(String mainModulePath) {
|
||||
this.mainSourcePath = mainModulePath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getMainSourcePath() {
|
||||
return mainSourcePath;
|
||||
}
|
||||
|
||||
public AndroidProject setModulePaths(List<String> modulePaths) {
|
||||
this.modulePaths = modulePaths;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getModulePaths() {
|
||||
if (modulePaths == null) modulePaths = new ArrayList<>();
|
||||
return modulePaths;
|
||||
}
|
||||
|
||||
public AndroidProject addModule(String path) {
|
||||
if (!getModulePaths().contains(path)) getModulePaths().add(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidProject setSourcePaths(List<String> sourcePaths) {
|
||||
this.sourcePaths = sourcePaths;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getSourcePaths() {
|
||||
if (sourcePaths == null) sourcePaths = new ArrayList<>();
|
||||
return sourcePaths;
|
||||
}
|
||||
|
||||
public AndroidProject addSource(String path) {
|
||||
if (!getSourcePaths().contains(path)) getSourcePaths().add(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidProject setClassPaths(List<String> classPaths) {
|
||||
this.classPaths = classPaths;
|
||||
return this;
|
||||
}
|
||||
|
||||
public List<String> getClassPaths() {
|
||||
if (classPaths == null) classPaths = new ArrayList<>();
|
||||
return classPaths;
|
||||
}
|
||||
|
||||
public AndroidProject addClasspath(String path) {
|
||||
if (!getClassPaths().contains(path)) getClassPaths().add(path);
|
||||
return this;
|
||||
}
|
||||
|
||||
public AndroidProject setAppName(String appName) {
|
||||
this.appName = appName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getAppName() {
|
||||
return appName;
|
||||
}
|
||||
|
||||
public AndroidProject setPackageName(String packageName) {
|
||||
this.packageName = packageName;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getPackageName() {
|
||||
return packageName;
|
||||
}
|
||||
|
||||
public AndroidProject setIconPath(String iconPath) {
|
||||
this.iconPath = iconPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getIconPath() {
|
||||
return iconPath;
|
||||
}
|
||||
|
||||
public AndroidProject setProjectPath(String projectPath) {
|
||||
this.projectPath = projectPath;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getProjectPath() {
|
||||
return projectPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel p1, int p2) {
|
||||
p1.writeString(appName);
|
||||
p1.writeString(packageName);
|
||||
p1.writeString(iconPath);
|
||||
p1.writeString(projectPath);
|
||||
p1.writeString(mainModule);
|
||||
p1.writeString(mainSourcePath);
|
||||
p1.writeList(modulePaths);
|
||||
p1.writeList(sourcePaths);
|
||||
p1.writeList(classPaths);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "["
|
||||
+ "\nappName="
|
||||
+ getAppName()
|
||||
+ "\npackageName="
|
||||
+ getPackageName()
|
||||
+ "\niconPath="
|
||||
+ getIconPath()
|
||||
+ "\nprojectPath="
|
||||
+ getProjectPath()
|
||||
+ "\nmainModule="
|
||||
+ getMainModule()
|
||||
+ "\nmainModulePath="
|
||||
+ getMainModulePath()
|
||||
+ "\nmainModuleSourcePath="
|
||||
+ getMainSourcePath()
|
||||
+ "\nmodules="
|
||||
+ getModulePaths()
|
||||
+ "\nsources="
|
||||
+ getSourcePaths()
|
||||
+ "\nclasspaths="
|
||||
+ getClassPaths()
|
||||
+ "\n]";
|
||||
}
|
||||
|
||||
public static final Creator<AndroidProject> CREATOR =
|
||||
new Creator<AndroidProject>() {
|
||||
|
||||
@Override
|
||||
public AndroidProject createFromParcel(Parcel p1) {
|
||||
return new AndroidProject(p1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AndroidProject[] newArray(int p1) {
|
||||
return new AndroidProject[p1];
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1,68 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.projects;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/**
|
||||
* Represents a module (subproject) of a root project.
|
||||
*
|
||||
* <p>In Android projects, a module can either be an application project, or a library project.
|
||||
*/
|
||||
public class IDEModule extends IDEProject {
|
||||
|
||||
/** Build tools version of this project */
|
||||
@SerializedName("buildToolsVersion")
|
||||
public String buildToolsVersion;
|
||||
|
||||
/**
|
||||
* Compile SDK version of this project
|
||||
*
|
||||
* <p>Representation: 'android-31', 'android-30', 'android-29', etc
|
||||
*/
|
||||
@SerializedName("compileSdkVersion")
|
||||
public String compileSdkVersion;
|
||||
|
||||
/** Minimum SDK version of this project */
|
||||
@SerializedName("minSdk")
|
||||
public SDK minSdk;
|
||||
|
||||
/** Target SDK version of this project */
|
||||
@SerializedName("targetSdk")
|
||||
public SDK targetSdk;
|
||||
|
||||
/** Version code of this project */
|
||||
@SerializedName("versionCode")
|
||||
public int versionCode = 0;
|
||||
|
||||
/** Version name of this project */
|
||||
@SerializedName("versionName")
|
||||
public String versionName = "Not defined";
|
||||
|
||||
/** Is this project an Android library project? */
|
||||
@SerializedName("isLibrary")
|
||||
public boolean isLibrary = true;
|
||||
|
||||
/** Does this module has viewBinding enabled? */
|
||||
@SerializedName("viewBindingEnabled")
|
||||
public boolean viewBindingEnabled;
|
||||
|
||||
/** Library projects don't have applicationId, application projects do. */
|
||||
@SerializedName("applicationId")
|
||||
public String applicationId;
|
||||
}
|
||||
@@ -1,96 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.projects;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
/** A model that contains data of a project currently opened in AndroidIDE */
|
||||
public class IDEProject {
|
||||
|
||||
/** Name of this project */
|
||||
@SerializedName("name")
|
||||
public String name = "";
|
||||
|
||||
/** Name of this project which should be displayed to user */
|
||||
@SerializedName("displayName")
|
||||
public String displayName = "";
|
||||
|
||||
/** Description of this project. Maybe null. */
|
||||
@SerializedName("description")
|
||||
public String description = "";
|
||||
|
||||
/**
|
||||
* Path of this project. This is NOT the directory path of this project.
|
||||
*
|
||||
* <p>For example, ':app or ':module:subModule'
|
||||
*/
|
||||
@SerializedName("path")
|
||||
public String path = "";
|
||||
|
||||
/** Path of the directory containing this project */
|
||||
@SerializedName("projectDir")
|
||||
public String projectDir;
|
||||
|
||||
/** File path of the application's icon We get this manually by parsing AndroidManifest.xml */
|
||||
public String iconPath;
|
||||
|
||||
/** Subprojects of this project */
|
||||
@SerializedName("modules")
|
||||
public List<IDEModule> modules = new ArrayList<>();
|
||||
|
||||
/** Tasks included in this project */
|
||||
@SerializedName("tasks")
|
||||
public List<IDETask> tasks = new ArrayList<>();
|
||||
|
||||
/** Path of jar file of dependencies of this project */
|
||||
@SerializedName("dependencies")
|
||||
public List<String> dependencies = new ArrayList<>();
|
||||
|
||||
/**
|
||||
* Get the module by its path
|
||||
*
|
||||
* @param path Path of the module
|
||||
* @return An Optional containing the IDEModule or Optional.empty()
|
||||
*/
|
||||
public Optional<IDEModule> getModuleByPath(String path) {
|
||||
if (modules != null && modules.size() > 0) {
|
||||
for (IDEModule module : modules) {
|
||||
if (module == null) continue;
|
||||
if (module.path.trim().equals(path.trim())) return Optional.of(module);
|
||||
}
|
||||
}
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
/** Stores information about a SDK. Like, • Codename • API string • API level */
|
||||
public static class SDK {
|
||||
|
||||
@SerializedName("codename")
|
||||
public String codename;
|
||||
|
||||
@SerializedName("apiString")
|
||||
public String apiString;
|
||||
|
||||
@SerializedName("apiLevel")
|
||||
public int apiLevel;
|
||||
}
|
||||
}
|
||||
@@ -1,44 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.projects;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
/** Represents a Gradle task */
|
||||
public class IDETask {
|
||||
|
||||
/** Name of this task */
|
||||
@SerializedName("name")
|
||||
public String name;
|
||||
|
||||
/** Description of this task */
|
||||
@SerializedName("description")
|
||||
public String description;
|
||||
|
||||
/** Name of the group that this task belongs to */
|
||||
@SerializedName("group")
|
||||
public String group;
|
||||
|
||||
/**
|
||||
* Path of this task
|
||||
*
|
||||
* <p>For example, ':app:assembleDebug', ':app:build'
|
||||
*/
|
||||
@SerializedName("path")
|
||||
public String path;
|
||||
}
|
||||
@@ -1,38 +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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.projects;
|
||||
|
||||
import android.os.FileObserver;
|
||||
|
||||
/**
|
||||
* A file observer that observer files in project directories. Sub classes need to provide the file
|
||||
* extensions that they want to get notified about.
|
||||
*
|
||||
* @author Akash Yadav
|
||||
*/
|
||||
public abstract class ProjectFileObserver extends FileObserver {
|
||||
// Cannot use constructors that accept java.io.File
|
||||
// They were added in API 29
|
||||
public ProjectFileObserver(String path) {
|
||||
this(path, ALL_EVENTS);
|
||||
}
|
||||
|
||||
public ProjectFileObserver(String path, int mask) {
|
||||
super(path, mask);
|
||||
}
|
||||
}
|
||||
@@ -36,16 +36,64 @@ import java.nio.file.Path
|
||||
object ProjectManager {
|
||||
private val log = com.itsaky.androidide.utils.ILogger.newInstance(javaClass.simpleName)
|
||||
var rootProject: IdeGradleProject? = null
|
||||
var projectPath: String? = null
|
||||
|
||||
fun getProjectDir(): String? =
|
||||
if (rootProject == null) projectPath else rootProject!!.projectDir!!.absolutePath
|
||||
|
||||
fun getApplicationModule(): IdeAndroidModule? {
|
||||
if (rootProject == null) {
|
||||
log.error(
|
||||
"No root project instance is set. Is the project initialization process finished?")
|
||||
return null
|
||||
}
|
||||
|
||||
val app = rootProject!!.findFirstAndroidModule()
|
||||
if (app == null) {
|
||||
log.error("No application module found in root project.")
|
||||
return null
|
||||
}
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
fun getApplicationResDirectories(): List<File>? {
|
||||
return collectResDirectories(getApplicationModule() ?: return null)
|
||||
}
|
||||
|
||||
fun collectResDirectories(android: IdeAndroidModule): List<File> {
|
||||
val main = android.mainSourceSet
|
||||
if (main == null) {
|
||||
log.error("No main source set found in application module: ${android.name}")
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val dirs = mutableListOf<File>()
|
||||
if (main.sourceProvider.resDirectories != null) {
|
||||
dirs.addAll(main.sourceProvider.resDirectories!!)
|
||||
}
|
||||
|
||||
val dependencies =
|
||||
collectProjectDependencies(rootProject!!, android)
|
||||
.filterIsInstance(IdeAndroidModule::class.java)
|
||||
|
||||
for (dependency in dependencies) {
|
||||
dirs.addAll(collectResDirectories(dependency))
|
||||
}
|
||||
|
||||
return dirs
|
||||
}
|
||||
|
||||
fun notifyProjectUpdate(server: JavaLanguageServer) {
|
||||
val sourceDirs = collectApplicationSourceDirs(rootProject!!)
|
||||
val classPaths = collectApplicationClassPaths(rootProject!!)
|
||||
val sourceDirs = collectApplicationSourceDirs()
|
||||
val classPaths = collectApplicationClassPaths()
|
||||
val configuration = JavaServerConfiguration(classPaths, sourceDirs)
|
||||
server.configurationChanged(configuration)
|
||||
}
|
||||
|
||||
private fun collectApplicationClassPaths(rootProject: IdeGradleProject): Set<Path> {
|
||||
val app = getApplicationModule(rootProject)
|
||||
private fun collectApplicationClassPaths(): Set<Path> {
|
||||
val app = getApplicationModule() ?: return emptySet()
|
||||
|
||||
val libraries = app.variantDependencies["debug"]!!.libraries
|
||||
val paths = mutableSetOf<Path>()
|
||||
|
||||
@@ -55,7 +103,8 @@ object ProjectManager {
|
||||
} else if (value.type == JAVA_LIBRARY) {
|
||||
paths.add(value.artifact!!.toPath())
|
||||
} else {
|
||||
val project = rootProject.findByPath(value.projectInfo!!.projectPath)!! as IdeModule
|
||||
val project =
|
||||
rootProject!!.findByPath(value.projectInfo!!.projectPath)!! as IdeModule
|
||||
paths.add(project.getGeneratedJar("debug").toPath())
|
||||
|
||||
if (project is IdeAndroidModule && project.projectPath != app.projectPath) {
|
||||
@@ -73,22 +122,16 @@ object ProjectManager {
|
||||
return paths
|
||||
}
|
||||
|
||||
private fun collectApplicationSourceDirs(rootProject: IdeGradleProject): Set<Path> {
|
||||
val app = getApplicationModule(rootProject)
|
||||
private fun collectApplicationSourceDirs(): Set<Path> {
|
||||
val app = getApplicationModule() ?: return emptySet()
|
||||
val sources = app.mainSourceSet ?: return emptySet()
|
||||
val javaDirs = sources.sourceProvider.javaDirectories
|
||||
val sourcePaths = javaDirs.toMutableSet()
|
||||
val projectSources = collectSourceDirs(collectProjectDependencies(rootProject, app))
|
||||
val projectSources = collectSourceDirs(collectProjectDependencies(rootProject!!, app))
|
||||
sourcePaths.addAll(projectSources)
|
||||
return sourcePaths.map { it.toPath() }.toSet()
|
||||
}
|
||||
|
||||
private fun getApplicationModule(rootProject: IdeGradleProject): IdeAndroidModule {
|
||||
return rootProject.findFirstAndroidModule()
|
||||
?: throw UnsupportedOperationException(
|
||||
"No application module found in project ${rootProject.name} (${rootProject.projectPath})")
|
||||
}
|
||||
|
||||
fun collectProjectDependencies(
|
||||
project: IdeGradleProject,
|
||||
app: IdeAndroidModule
|
||||
|
||||
@@ -1,83 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
package com.itsaky.androidide.services.builder;
|
||||
|
||||
import com.itsaky.androidide.projects.IDEModule;
|
||||
import com.itsaky.androidide.projects.IDEProject;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/** A client should implement this interface to get callbacks about a build */
|
||||
public interface BuildListener {
|
||||
/** Called to notify the client about the modification of build/generated files */
|
||||
void onBuildModified();
|
||||
|
||||
/**
|
||||
* Called when the project has been loaded and its data has been read
|
||||
*
|
||||
* @param project The loaded project
|
||||
* @param appModule The main application module
|
||||
*/
|
||||
void onProjectLoaded(IDEProject project, Optional<IDEModule> appModule);
|
||||
|
||||
/**
|
||||
* Called when a Gradle Daemon is being started
|
||||
*
|
||||
* @param task Task that is currently running
|
||||
*/
|
||||
void onStartingGradleDaemon(GradleTask task);
|
||||
|
||||
/**
|
||||
* Called when a task is started
|
||||
*
|
||||
* @param task Task that is currently running
|
||||
* @param name Name of this task that should be shown to user
|
||||
*/
|
||||
void onRunTask(GradleTask task, String name);
|
||||
|
||||
/**
|
||||
* Notifys about a successful build
|
||||
*
|
||||
* @param task Task that succeeded
|
||||
* @param msg Successful message
|
||||
*/
|
||||
void onBuildSuccessful(GradleTask task, String msg);
|
||||
|
||||
/**
|
||||
* Notifys about a failed build
|
||||
*
|
||||
* @param task Task that failed
|
||||
* @param msg Failure message
|
||||
*/
|
||||
void onBuildFailed(GradleTask task, String msg);
|
||||
|
||||
/** Called to notify client to save any opened files */
|
||||
void saveFiles();
|
||||
|
||||
/**
|
||||
* Notifys the client to append the build output that should be shown to user
|
||||
*
|
||||
* @param task Task that is currently running
|
||||
* @param text Output to append
|
||||
*/
|
||||
void appendOutput(GradleTask task, CharSequence text);
|
||||
|
||||
/** Called to notify the client to prepare for a build */
|
||||
void prepareBuild();
|
||||
}
|
||||
@@ -1,493 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.itsaky.androidide.services.builder;
|
||||
|
||||
import static com.itsaky.androidide.managers.ToolsManager.getCommonAsset;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.StringRes;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.blankj.utilcode.util.ResourceUtils;
|
||||
import com.blankj.utilcode.util.ZipUtils;
|
||||
import com.google.gson.Gson;
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.managers.PreferenceManager;
|
||||
import com.itsaky.androidide.projects.IDEModule;
|
||||
import com.itsaky.androidide.projects.IDEProject;
|
||||
import com.itsaky.androidide.shell.IProcessExecutor;
|
||||
import com.itsaky.androidide.shell.IProcessExitListener;
|
||||
import com.itsaky.androidide.shell.ProcessExecutorFactory;
|
||||
import com.itsaky.androidide.shell.ProcessStreamsHolder;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
import com.itsaky.androidide.tasks.gradle.BaseGradleTasks;
|
||||
import com.itsaky.androidide.tasks.gradle.resources.UpdateResourceClassesTask;
|
||||
import com.itsaky.androidide.utils.Environment;
|
||||
import com.itsaky.androidide.utils.InputStreamLineReader;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
import com.itsaky.toaster.Toaster;
|
||||
|
||||
import org.jetbrains.annotations.Contract;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.text.DateFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class IDEService {
|
||||
|
||||
public static final int TASK_SHOW_DEPENDENCIES = GradleTask.TASK_SHOW_DEPENDENCIES;
|
||||
public static final int TASK_ASSEMBLE_DEBUG = GradleTask.TASK_ASSEMBLE_DEBUG;
|
||||
public static final int TASK_ASSEMBLE_RELEASE = GradleTask.TASK_ASSEMBLE_RELEASE;
|
||||
public static final int TASK_BUILD = GradleTask.TASK_BUILD;
|
||||
public static final int TASK_BUNDLE = GradleTask.TASK_BUNDLE;
|
||||
public static final int TASK_CLEAN = GradleTask.TASK_CLEAN;
|
||||
public static final int TASK_CLEAN_BUILD = GradleTask.TASK_CLEAN_BUILD;
|
||||
public static final int TASK_DEX = GradleTask.TASK_DEX;
|
||||
public static final int TASK_LINT = GradleTask.TASK_LINT;
|
||||
public static final int TASK_LINT_DEBUG = GradleTask.TASK_LINT_DEBUG;
|
||||
public static final int TASK_LINT_RELEASE = GradleTask.TASK_LINT_RELEASE;
|
||||
public static final ILogger LOG = ILogger.newInstance("IDEService");
|
||||
private final File projectRoot;
|
||||
private final IProcessExecutor processExecutor;
|
||||
private BuildListener listener;
|
||||
private GradleTask currentTask;
|
||||
private IDEProject mIDEProject;
|
||||
private IDEModule mAppModule;
|
||||
private final InputStreamLineReader.OnReadListener mOutputReadListener = this::onBuildOutput;
|
||||
private boolean isBuilding = false;
|
||||
private final IProcessExitListener mProcessExitListener =
|
||||
new IProcessExitListener() {
|
||||
@Override
|
||||
public void onExit(int code) {
|
||||
isBuilding = false;
|
||||
if (listener != null) {
|
||||
final String msg = getString(R.string.msg_gradle_terminated, code);
|
||||
if (code == 0) {
|
||||
onBuildSuccessful(currentTask, msg);
|
||||
} else {
|
||||
onBuildFailed(currentTask, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public IDEService(File projectRoot) {
|
||||
this.projectRoot = projectRoot;
|
||||
this.processExecutor = ProcessExecutorFactory.commonExecutor();
|
||||
}
|
||||
|
||||
public IDEService setListener(final BuildListener listener) {
|
||||
this.listener = listener == null ? null : new MainThreadBuildListener(listener);
|
||||
return this;
|
||||
}
|
||||
|
||||
protected void onBuildOutput(String line) {
|
||||
if (listener == null || line == null) return;
|
||||
line = line.trim();
|
||||
|
||||
String PROJECT_INITIALIZED = ">>> PROJECT INITIALIZED <<<";
|
||||
if (line.contains(PROJECT_INITIALIZED)) {
|
||||
readIdeProject();
|
||||
return;
|
||||
}
|
||||
|
||||
String text = line;
|
||||
|
||||
final var app = StudioApp.getInstance();
|
||||
// Try to shorten common file paths
|
||||
if (line.contains(app.getRootDir().getAbsolutePath())) {
|
||||
text = line.replace(app.getRootDir().getAbsolutePath(), "HOME");
|
||||
}
|
||||
if (line.contains(
|
||||
Objects.requireNonNull(app.getRootDir().getParentFile()).getAbsolutePath())) {
|
||||
text = line.replace(app.getRootDir().getParentFile().getAbsolutePath(), "ROOT");
|
||||
}
|
||||
|
||||
listener.appendOutput(currentTask, text);
|
||||
|
||||
String RUN_TASK = "> Task";
|
||||
String STARTING_DAEMON = "Starting a ";
|
||||
if (line.startsWith(RUN_TASK)) {
|
||||
listener.onRunTask(currentTask, line.trim());
|
||||
} else if (line.startsWith(STARTING_DAEMON)) {
|
||||
listener.onStartingGradleDaemon(currentTask);
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBuildSuccessful(GradleTask task, String msg) {
|
||||
if (currentTask != null && currentTask.getTaskID() != TASK_SHOW_DEPENDENCIES) {
|
||||
listener.onBuildSuccessful(task, msg.trim());
|
||||
}
|
||||
appendOutputSeparator();
|
||||
|
||||
if (currentTask != null && currentTask.affectsGeneratedSources()) {
|
||||
notifyExternalSourceChange();
|
||||
}
|
||||
}
|
||||
|
||||
protected void onBuildFailed(GradleTask task, @NonNull String msg) {
|
||||
isBuilding = false;
|
||||
listener.onBuildFailed(task, msg.trim());
|
||||
appendOutputSeparator();
|
||||
}
|
||||
|
||||
private void readIdeProject() {
|
||||
// Parsing project data is meaningless if there is no one listening...
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final BuildListener local = listener;
|
||||
new Thread(
|
||||
() -> {
|
||||
mIDEProject =
|
||||
new Gson()
|
||||
.fromJson(
|
||||
FileIOUtils.readFile2String(
|
||||
Environment.PROJECT_DATA_FILE),
|
||||
IDEProject.class);
|
||||
if (mIDEProject != null) {
|
||||
Optional<IDEModule> appModule = mIDEProject.getModuleByPath(":app");
|
||||
appModule.ifPresent(ideModule -> mAppModule = ideModule);
|
||||
local.onProjectLoaded(mIDEProject, appModule);
|
||||
} else {
|
||||
StudioApp.getInstance()
|
||||
.toast(
|
||||
com.itsaky.androidide.R.string
|
||||
.msg_init_project_failed,
|
||||
Toaster.Type.ERROR);
|
||||
}
|
||||
})
|
||||
.start();
|
||||
}
|
||||
|
||||
private void appendOutputSeparator() {
|
||||
listener.appendOutput(currentTask, "\n\n");
|
||||
}
|
||||
|
||||
public void exit() {
|
||||
// Delete the tmp directory. It is not needed anymore...
|
||||
FileUtils.delete(Environment.TMP_DIR);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String currentTime() {
|
||||
String pattern = "HH:mm:ss";
|
||||
DateFormat df = new SimpleDateFormat(pattern, Locale.US);
|
||||
Date today = Calendar.getInstance().getTime();
|
||||
return df.format(today);
|
||||
}
|
||||
|
||||
private String getString(@StringRes int id) {
|
||||
return StudioApp.getInstance().getString(id);
|
||||
}
|
||||
|
||||
private String getString(@StringRes int id, Object... format) {
|
||||
return StudioApp.getInstance().getString(id, format);
|
||||
}
|
||||
|
||||
private void notifyExternalSourceChange() {
|
||||
// TODO Watch for file changes using a file observer
|
||||
// instead of depending on the build
|
||||
if (listener != null) {
|
||||
listener.onBuildModified();
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private String[] getArguments(List<String> tasks) {
|
||||
final PreferenceManager prefs = StudioApp.getInstance().getPrefManager();
|
||||
final List<String> args = new ArrayList<>();
|
||||
|
||||
// 'bash' shell is always preferred
|
||||
if (Environment.SHELL.exists()) {
|
||||
args.add(Environment.SHELL.getAbsolutePath());
|
||||
} else {
|
||||
args.add(Environment.BUSYBOX.getAbsolutePath());
|
||||
args.add("sh");
|
||||
}
|
||||
|
||||
args.add(new File(projectRoot, "gradlew").getAbsolutePath());
|
||||
args.addAll(asAppTasks(tasks));
|
||||
args.add("--init-script");
|
||||
args.add(Environment.INIT_SCRIPT.getAbsolutePath());
|
||||
|
||||
if (prefs.isStackTraceEnabled()) {
|
||||
args.add("--stacktrace");
|
||||
}
|
||||
if (prefs.isGradleInfoEnabled()) {
|
||||
args.add("--info");
|
||||
}
|
||||
if (prefs.isGradleDebugEnabled()) {
|
||||
args.add("--debug");
|
||||
}
|
||||
if (prefs.isGradleScanEnabled()) {
|
||||
args.add("--scan");
|
||||
}
|
||||
if (prefs.isGradleWarningEnabled()) {
|
||||
args.add("--warning-mode");
|
||||
args.add("all");
|
||||
}
|
||||
if (prefs.isGradleBuildCacheEnabled()) {
|
||||
args.add("--build-cache");
|
||||
}
|
||||
if (prefs.isGradleOfflineModeEnabled()) {
|
||||
args.add("--offline");
|
||||
}
|
||||
|
||||
return args.toArray(new String[0]);
|
||||
}
|
||||
|
||||
private Collection<? extends String> asAppTasks(@NonNull List<String> tasks) {
|
||||
return tasks.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(this::asAppModuleTask)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
@Contract(pure = true)
|
||||
private String asAppModuleTask(@NonNull String name) {
|
||||
return name.startsWith(":app:") ? name : ":app:" + name;
|
||||
}
|
||||
|
||||
public void execTask(GradleTask task) {
|
||||
execTask(task, false);
|
||||
}
|
||||
|
||||
public void execTask(GradleTask task, boolean stopIfRunning) {
|
||||
if (stopIfRunning && isBuilding()) {
|
||||
stopAllDaemons();
|
||||
} else if (isBuilding()) {
|
||||
return;
|
||||
}
|
||||
|
||||
execTask(
|
||||
task,
|
||||
isGradleWrapperAvailable()
|
||||
? CompletableFuture.completedFuture(Boolean.TRUE)
|
||||
: installWrapper());
|
||||
}
|
||||
|
||||
private void execTask(GradleTask task, CompletableFuture<Boolean> installing) {
|
||||
|
||||
final Runnable taskRunner =
|
||||
() -> {
|
||||
if (installing != null) {
|
||||
Boolean result;
|
||||
String failure = "";
|
||||
try {
|
||||
result = installing.get();
|
||||
} catch (Throwable th) {
|
||||
result = Boolean.FALSE;
|
||||
failure = th.getMessage();
|
||||
}
|
||||
|
||||
if (result.equals(Boolean.FALSE)) {
|
||||
if (listener != null) {
|
||||
listener.appendOutput(
|
||||
task, "-------------------- ERROR --------------------");
|
||||
listener.appendOutput(
|
||||
task,
|
||||
getString(
|
||||
R.string.msg_gradlew_installation_failed, failure));
|
||||
listener.appendOutput(
|
||||
task, "-----------------------------------------------");
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (listener != null) {
|
||||
|
||||
final ProcessStreamsHolder holder = new ProcessStreamsHolder();
|
||||
|
||||
listener.saveFiles();
|
||||
|
||||
currentTask = task;
|
||||
listener.appendOutput(
|
||||
task,
|
||||
getString(R.string.msg_task_begin, currentTime(), task.getName()));
|
||||
try {
|
||||
|
||||
Environment.mkdirIfNotExits(Environment.TMP_DIR);
|
||||
|
||||
processExecutor.execAsync(
|
||||
holder,
|
||||
mProcessExitListener,
|
||||
projectRoot.getAbsolutePath(),
|
||||
true,
|
||||
getArguments(task.getTasks()));
|
||||
isBuilding = true;
|
||||
|
||||
startReading(holder.in);
|
||||
|
||||
listener.prepareBuild();
|
||||
} catch (Throwable e) {
|
||||
listener.onBuildFailed(task, e.getMessage());
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
final Thread thread = new Thread(taskRunner, "GradleTaskExecutor");
|
||||
thread.setDaemon(true);
|
||||
thread.start();
|
||||
}
|
||||
|
||||
private void startReading(InputStream in) {
|
||||
final Thread reader =
|
||||
new Thread(
|
||||
new InputStreamLineReader(in, mOutputReadListener),
|
||||
"GradleProcessOutputReader");
|
||||
reader.setDaemon(true);
|
||||
reader.start();
|
||||
}
|
||||
|
||||
private CompletableFuture<Boolean> installWrapper() {
|
||||
if (listener != null) {
|
||||
listener.appendOutput(null, "-------------------- NOTE --------------------");
|
||||
listener.appendOutput(null, getString(R.string.msg_installing_gradlew));
|
||||
listener.appendOutput(null, "----------------------------------------------");
|
||||
}
|
||||
|
||||
return CompletableFuture.supplyAsync(
|
||||
() -> {
|
||||
final File extracted = new File(Environment.TMP_DIR, "gradle-wrapper.zip");
|
||||
if (ResourceUtils.copyFileFromAssets(
|
||||
getCommonAsset("gradle-wrapper.zip"), extracted.getAbsolutePath())) {
|
||||
try {
|
||||
final List<File> files = ZipUtils.unzipFile(extracted, projectRoot);
|
||||
if (files != null && !files.isEmpty()) {
|
||||
return Boolean.TRUE;
|
||||
}
|
||||
} catch (IOException e) {
|
||||
return Boolean.FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
return Boolean.FALSE;
|
||||
});
|
||||
}
|
||||
|
||||
public boolean isGradleWrapperAvailable() {
|
||||
if (projectRoot == null || !projectRoot.exists()) {
|
||||
return false;
|
||||
}
|
||||
final File gradlew = new File(projectRoot, "gradlew");
|
||||
final File gradleWrapperJar = new File(projectRoot, "gradle/wrapper/gradle-wrapper.jar");
|
||||
final File gradleWrapperProps =
|
||||
new File(projectRoot, "gradle/wrapper/gradle-wrapper.properties");
|
||||
|
||||
return gradlew.exists() && gradleWrapperJar.exists() && gradleWrapperProps.exists();
|
||||
}
|
||||
|
||||
public void assembleDebug() {
|
||||
execTask(BaseGradleTasks.ASSEMBLE_DEBUG);
|
||||
}
|
||||
|
||||
public void assembleRelease() {
|
||||
execTask(BaseGradleTasks.ASSEMBLE_RELEASE);
|
||||
}
|
||||
|
||||
public void build() {
|
||||
execTask(BaseGradleTasks.BUILD);
|
||||
}
|
||||
|
||||
public void bundle() {
|
||||
execTask(BaseGradleTasks.BUNDLE);
|
||||
}
|
||||
|
||||
public void clean() {
|
||||
execTask(BaseGradleTasks.CLEAN);
|
||||
}
|
||||
|
||||
public void cleanAndRebuild() {
|
||||
execTask(BaseGradleTasks.CLEAN_BUILD);
|
||||
}
|
||||
|
||||
public void stopAllDaemons() {
|
||||
StudioApp.getInstance()
|
||||
.newShell(null)
|
||||
.bgAppend(
|
||||
String.format(
|
||||
Locale.US,
|
||||
"cd '%s' && sh gradlew --stop",
|
||||
projectRoot.getAbsolutePath()));
|
||||
if (isBuilding()) {
|
||||
if (listener != null)
|
||||
listener.onBuildFailed(currentTask, getString(R.string.msg_daemons_stopped));
|
||||
isBuilding = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void lint() {
|
||||
execTask(BaseGradleTasks.LINT);
|
||||
}
|
||||
|
||||
public void lintDebug() {
|
||||
execTask(BaseGradleTasks.LINT_DEBUG);
|
||||
}
|
||||
|
||||
public void lintRelease() {
|
||||
execTask(BaseGradleTasks.LINT_RELEASE);
|
||||
}
|
||||
|
||||
public void updateResourceClasses() {
|
||||
execTask(
|
||||
new UpdateResourceClassesTask(mAppModule != null && mAppModule.viewBindingEnabled));
|
||||
}
|
||||
|
||||
public boolean isBuilding() {
|
||||
return isBuilding;
|
||||
}
|
||||
|
||||
public String typeString(int type) {
|
||||
switch (type) {
|
||||
case TASK_ASSEMBLE_DEBUG:
|
||||
return getString(R.string.build_debug);
|
||||
case TASK_ASSEMBLE_RELEASE:
|
||||
return getString(R.string.build_release);
|
||||
case TASK_BUILD:
|
||||
return getString(R.string.build);
|
||||
case TASK_CLEAN_BUILD:
|
||||
return getString(R.string.clean_amp_build);
|
||||
case TASK_CLEAN:
|
||||
return getString(R.string.clean_project);
|
||||
case TASK_BUNDLE:
|
||||
return getString(R.string.create_aab);
|
||||
case TASK_DEX:
|
||||
return getString(R.string.compiling);
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
package com.itsaky.androidide.services.builder;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.itsaky.androidide.projects.IDEModule;
|
||||
import com.itsaky.androidide.projects.IDEProject;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
class MainThreadBuildListener implements BuildListener {
|
||||
|
||||
private final BuildListener listener;
|
||||
|
||||
public MainThreadBuildListener(@NonNull final BuildListener listener) {
|
||||
Objects.requireNonNull(listener, "Build listener cannot be null");
|
||||
this.listener = listener;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuildModified() {
|
||||
ThreadUtils.runOnUiThread(listener::onBuildModified);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProjectLoaded(IDEProject project, Optional<IDEModule> appModule) {
|
||||
ThreadUtils.runOnUiThread(() -> listener.onProjectLoaded(project, appModule));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStartingGradleDaemon(GradleTask task) {
|
||||
ThreadUtils.runOnUiThread(() -> listener.onStartingGradleDaemon(task));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunTask(GradleTask task, String name) {
|
||||
ThreadUtils.runOnUiThread(() -> listener.onRunTask(task, name));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuildSuccessful(GradleTask task, String msg) {
|
||||
ThreadUtils.runOnUiThread(() -> listener.onBuildSuccessful(task, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuildFailed(GradleTask task, String msg) {
|
||||
ThreadUtils.runOnUiThread(() -> listener.onBuildFailed(task, msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void saveFiles() {
|
||||
ThreadUtils.runOnUiThread(listener::saveFiles);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendOutput(GradleTask task, CharSequence text) {
|
||||
ThreadUtils.runOnUiThread(() -> listener.appendOutput(task, text));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void prepareBuild() {
|
||||
ThreadUtils.runOnUiThread(listener::prepareBuild);
|
||||
}
|
||||
}
|
||||
@@ -1,46 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks;
|
||||
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
|
||||
public abstract class BaseGradleTask implements GradleTask {
|
||||
|
||||
@Override
|
||||
public boolean affectsGeneratedSources() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldSaveFiles() {
|
||||
return getType() == Type.BUILD
|
||||
|| getType() == Type.LINT
|
||||
|| getTaskID() == IDEService.TASK_DEX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
return obj != null
|
||||
&& obj instanceof GradleTask
|
||||
&& getTaskID() == ((GradleTask) obj).getTaskID()
|
||||
&& getType() == ((GradleTask) obj).getType();
|
||||
}
|
||||
}
|
||||
@@ -1,68 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface GradleTask {
|
||||
|
||||
public String getName();
|
||||
|
||||
public String getCommands();
|
||||
|
||||
public List<String> getTasks();
|
||||
|
||||
public int getTaskID();
|
||||
|
||||
public boolean affectsGeneratedSources();
|
||||
|
||||
public boolean shouldSaveFiles();
|
||||
|
||||
public boolean canOutput();
|
||||
|
||||
public Type getType();
|
||||
|
||||
public static enum Type {
|
||||
ANDROIDIDE_PLUGIN,
|
||||
BUILD,
|
||||
LINT,
|
||||
HELP,
|
||||
OTHER
|
||||
}
|
||||
|
||||
public static final int TASK_SHOW_DEPENDENCIES = 0;
|
||||
public static final int TASK_ASSEMBLE_DEBUG = 1;
|
||||
public static final int TASK_ASSEMBLE_RELEASE = 2;
|
||||
public static final int TASK_BUILD = 3;
|
||||
public static final int TASK_BUNDLE = 4;
|
||||
public static final int TASK_CLEAN = 5;
|
||||
public static final int TASK_CLEAN_BUILD = 6;
|
||||
public static final int TASK_COMPILE_JAVA = 7;
|
||||
public static final int TASK_DEX = 8;
|
||||
public static final int TASK_LINT = 9;
|
||||
public static final int TASK_LINT_DEBUG = 10;
|
||||
public static final int TASK_LINT_RELEASE = 11;
|
||||
public static final int TASK_TASKS = 12;
|
||||
public static final int TASK_INIT_PROJECT = 13;
|
||||
public static final int TASK_PROCESS_DEBUG_RESOURCES = 14;
|
||||
public static final int DATABINDING_GEN_BASE_CLASSES_DEBUG = 15;
|
||||
public static final int TASK_UPDATE_RESOURCE_CLASSES = 16;
|
||||
}
|
||||
@@ -1,49 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle;
|
||||
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.tasks.gradle.build.AssembleDebug;
|
||||
import com.itsaky.androidide.tasks.gradle.build.AssembleRelease;
|
||||
import com.itsaky.androidide.tasks.gradle.build.Build;
|
||||
import com.itsaky.androidide.tasks.gradle.build.Bundle;
|
||||
import com.itsaky.androidide.tasks.gradle.build.Clean;
|
||||
import com.itsaky.androidide.tasks.gradle.build.CleanBuild;
|
||||
import com.itsaky.androidide.tasks.gradle.lint.Lint;
|
||||
import com.itsaky.androidide.tasks.gradle.lint.LintDebug;
|
||||
import com.itsaky.androidide.tasks.gradle.lint.LintRelease;
|
||||
import com.itsaky.androidide.tasks.gradle.plugin.InitializeIDEProject;
|
||||
import com.itsaky.androidide.tasks.gradle.resources.ProcessDebugResources;
|
||||
|
||||
public class BaseGradleTasks {
|
||||
|
||||
public static final BaseGradleTask BUILD = new Build(),
|
||||
ASSEMBLE_DEBUG = new AssembleDebug(),
|
||||
ASSEMBLE_RELEASE = new AssembleRelease(),
|
||||
BUNDLE = new Bundle(),
|
||||
CLEAN = new Clean(),
|
||||
CLEAN_BUILD = new CleanBuild(),
|
||||
LINT = new Lint(),
|
||||
LINT_DEBUG = new LintDebug(),
|
||||
LINT_RELEASE = new LintRelease(),
|
||||
PROCESS_DEBUG_RESOURCES = new ProcessDebugResources(),
|
||||
INITIALIZE_IDE_PROJECT = new InitializeIDEProject();
|
||||
}
|
||||
@@ -1,153 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.build;
|
||||
|
||||
import static com.itsaky.androidide.models.ApkMetadata.ArtifactType;
|
||||
import static com.itsaky.androidide.models.ApkMetadata.Element;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.blankj.utilcode.util.FileUtils;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.models.ApkMetadata;
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.utils.JSONUtility;
|
||||
import com.itsaky.androidide.utils.ILogger;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public abstract class ApkGeneratingTask extends BaseGradleTask {
|
||||
|
||||
private static final ILogger LOG = ILogger.newInstance("ApkGeneratingTask");
|
||||
|
||||
/**
|
||||
* Get the directories where APKs are generated. These directories must contain an
|
||||
* output-metadata.json file
|
||||
*/
|
||||
protected abstract Set<File> provideApkDirectories(File buildDir);
|
||||
|
||||
protected Set<File> directories(File buildDir) {
|
||||
if (buildDir == null || !buildDir.exists() || !buildDir.isDirectory()) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return provideApkDirectories(buildDir);
|
||||
}
|
||||
|
||||
/** Get generated APKs */
|
||||
public Set<File> getApks(File buildDir) {
|
||||
final Set<File> result = new HashSet<>();
|
||||
final Set<File> dirs = directories(buildDir);
|
||||
if (dirs == null || dirs.isEmpty()) {
|
||||
LOG.error("No apk directories found. buildDir=" + buildDir);
|
||||
return result;
|
||||
}
|
||||
|
||||
for (File dir : dirs) {
|
||||
final File jsonData = new File(dir, "output-metadata.json");
|
||||
if (!jsonData.exists() || !FileUtils.isUtf8(jsonData)) {
|
||||
LOG.error(
|
||||
"No output-metadata.json file found in APK output directory"
|
||||
+ " or the file is empty. APK directory:",
|
||||
dir);
|
||||
continue;
|
||||
}
|
||||
|
||||
final String contents = FileIOUtils.readFile2String(jsonData);
|
||||
|
||||
try {
|
||||
final ApkMetadata metadata = JSONUtility.gson.fromJson(contents, ApkMetadata.class);
|
||||
if (!isValid(metadata)) {
|
||||
LOG.warn("Invalid APK metadata:", metadata);
|
||||
continue;
|
||||
}
|
||||
|
||||
for (Element element : metadata.getElements()) {
|
||||
if (element == null || element.getOutputFile() == null) {
|
||||
LOG.warn("No output file specified in APK metadata element:", element);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.getOutputFile().endsWith(".apk")) {
|
||||
final File apk = new File(dir, element.getOutputFile());
|
||||
if (apk.exists() && apk.isFile()) {
|
||||
LOG.info("APK generated:", apk);
|
||||
result.add(apk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Throwable th) {
|
||||
LOG.error(
|
||||
StudioApp.getInstance()
|
||||
.getString(
|
||||
com.itsaky.androidide.R.string
|
||||
.err_cannot_parse_apk_metadata),
|
||||
th);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean isValid(ApkMetadata metadata) {
|
||||
|
||||
// Null checks
|
||||
if (metadata == null
|
||||
|| metadata.getArtifactType() == null
|
||||
|| metadata.getArtifactType().getType() == null
|
||||
|| metadata.getElements() == null) {
|
||||
LOG.warn("APK metadata null check failed. Metadata:", metadata);
|
||||
return false;
|
||||
}
|
||||
|
||||
final ArtifactType type = metadata.getArtifactType();
|
||||
final List<Element> elements = metadata.getElements();
|
||||
|
||||
if (!type.getType().equals(ArtifactType.TYPE_APK)) {
|
||||
LOG.warn("Artifact is not of type APK. Metadata:", metadata);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (elements.isEmpty()) {
|
||||
LOG.warn("No output elements found for metadata:", metadata);
|
||||
return false;
|
||||
}
|
||||
|
||||
boolean atLeastOneApk = false;
|
||||
for (Element element : elements) {
|
||||
if (element == null
|
||||
|| element.getOutputFile() == null
|
||||
|| !element.getOutputFile().endsWith(".apk")) {
|
||||
LOG.warn("Skipping output element because file is not APK:", element);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (element.getOutputFile().endsWith(".apk")) {
|
||||
atLeastOneApk = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug("Output metadata validation succeeded");
|
||||
return atLeastOneApk;
|
||||
}
|
||||
}
|
||||
@@ -1,69 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.build;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class AssembleDebug extends ApkGeneratingTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.build_debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<File> provideApkDirectories(File buildDir) {
|
||||
final Set<File> dirs = new HashSet<>();
|
||||
dirs.add(new File(buildDir, "outputs/apk/debug"));
|
||||
return dirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "assembleDebug";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Collections.singletonList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_ASSEMBLE_DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.BUILD;
|
||||
}
|
||||
}
|
||||
@@ -1,72 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.build;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class AssembleRelease extends ApkGeneratingTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.build_release);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<File> provideApkDirectories(File buildDir) {
|
||||
final Set<File> dirs = new HashSet<>();
|
||||
dirs.add(new File(buildDir, "outputs/apk/release"));
|
||||
return dirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "assembleRelease";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_ASSEMBLE_RELEASE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return GradleTask.Type.BUILD;
|
||||
}
|
||||
}
|
||||
@@ -1,73 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.build;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class Build extends ApkGeneratingTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.build);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Set<File> provideApkDirectories(File buildDir) {
|
||||
final Set<File> dirs = new HashSet<>();
|
||||
dirs.add(new File(buildDir, "outputs/apk/debug"));
|
||||
dirs.add(new File(buildDir, "outputs/apk/release"));
|
||||
return dirs;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "build";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_BUILD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return GradleTask.Type.BUILD;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.build;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Bundle extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.create_aab);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "bundle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_BUNDLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return GradleTask.Type.BUILD;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.build;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Clean extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.clean_project);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "clean";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_CLEAN;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return GradleTask.Type.BUILD;
|
||||
}
|
||||
}
|
||||
@@ -1,63 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.build;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class CleanBuild extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.clean_amp_build);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "clean build";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList("clean", "build");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_CLEAN_BUILD;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return GradleTask.Type.BUILD;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.lint;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class Lint extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.lint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "lint";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList("lint");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_LINT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.LINT;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.lint;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
public class LintDebug extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.lint_debug);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "lintDebug";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Collections.singletonList("lintDebug");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_LINT_DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.LINT;
|
||||
}
|
||||
}
|
||||
@@ -1,62 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.lint;
|
||||
|
||||
import com.itsaky.androidide.R;
|
||||
import com.itsaky.androidide.app.StudioApp;
|
||||
import com.itsaky.androidide.services.builder.IDEService;
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class LintRelease extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return StudioApp.getInstance().getString(R.string.lint_release);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "lintRelease";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList("lintRelease");
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return IDEService.TASK_LINT_RELEASE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.LINT;
|
||||
}
|
||||
}
|
||||
@@ -1,60 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.plugin;
|
||||
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class InitializeIDEProject extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "initializeIDEProject";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "initializeIDEProject";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return TASK_INIT_PROJECT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return Type.ANDROIDIDE_PLUGIN;
|
||||
}
|
||||
}
|
||||
-64
@@ -1,64 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.resources;
|
||||
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class DatabindingGenBaseClassesDebug extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public boolean affectsGeneratedSources() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Generate databinding classes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "databindingGenBaseClassesDebug";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return DATABINDING_GEN_BASE_CLASSES_DEBUG;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Type getType() {
|
||||
return Type.BUILD;
|
||||
}
|
||||
}
|
||||
-65
@@ -1,65 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.resources;
|
||||
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public class ProcessDebugResources extends BaseGradleTask {
|
||||
|
||||
@Override
|
||||
public boolean affectsGeneratedSources() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "processDebugResources";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
return "processDebugResources";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
return Arrays.asList(getCommands());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return TASK_PROCESS_DEBUG_RESOURCES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return Type.BUILD;
|
||||
}
|
||||
}
|
||||
-82
@@ -1,82 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.tasks.gradle.resources;
|
||||
|
||||
import com.itsaky.androidide.tasks.BaseGradleTask;
|
||||
import com.itsaky.androidide.tasks.GradleTask;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class UpdateResourceClassesTask extends BaseGradleTask {
|
||||
|
||||
private final boolean viewBinding;
|
||||
|
||||
public UpdateResourceClassesTask(boolean viewBinding) {
|
||||
this.viewBinding = viewBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean affectsGeneratedSources() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "Update resource classes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommands() {
|
||||
String command = "";
|
||||
if (viewBinding) {
|
||||
command = new DatabindingGenBaseClassesDebug().getCommands();
|
||||
command += " ";
|
||||
}
|
||||
command += new ProcessDebugResources().getCommands();
|
||||
return command;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getTasks() {
|
||||
List<String> tasks = new ArrayList<>();
|
||||
if (viewBinding) {
|
||||
tasks.addAll(new DatabindingGenBaseClassesDebug().getTasks());
|
||||
}
|
||||
tasks.addAll(new ProcessDebugResources().getTasks());
|
||||
return tasks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getTaskID() {
|
||||
return TASK_UPDATE_RESOURCE_CLASSES;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canOutput() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public GradleTask.Type getType() {
|
||||
return Type.BUILD;
|
||||
}
|
||||
}
|
||||
@@ -37,9 +37,9 @@ package com.itsaky.androidide.utils
|
||||
import android.content.Context
|
||||
import com.itsaky.androidide.actions.ActionItem
|
||||
import com.itsaky.androidide.actions.ActionsRegistry
|
||||
import com.itsaky.androidide.actions.build.CancelBuildAction
|
||||
import com.itsaky.androidide.actions.build.QuickRunAction
|
||||
import com.itsaky.androidide.actions.build.RunActionMenu
|
||||
import com.itsaky.androidide.actions.build.CancelBuildAction
|
||||
import com.itsaky.androidide.actions.editor.CopyAction
|
||||
import com.itsaky.androidide.actions.editor.CutAction
|
||||
import com.itsaky.androidide.actions.editor.ExpandSelectionAction
|
||||
|
||||
@@ -1,201 +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 <https://www.gnu.org/licenses/>.
|
||||
*
|
||||
**************************************************************************************/
|
||||
|
||||
package com.itsaky.androidide.utils;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.blankj.utilcode.util.FileIOUtils;
|
||||
import com.itsaky.androidide.projects.AndroidProject;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import fr.arnaudguyon.xmltojsonlib.XmlToJson;
|
||||
|
||||
public class ProjectFinder {
|
||||
|
||||
public static AndroidProject fromFolder(File file) {
|
||||
if (!file.isDirectory() || file.isHidden()) return null;
|
||||
File settings = new File(file, "settings.gradle");
|
||||
if (!settings.exists()) return null;
|
||||
String content = FileUtil.readFile(settings.getAbsolutePath());
|
||||
if (TextUtils.isEmpty(content)) return null;
|
||||
|
||||
AndroidProject project = new AndroidProject();
|
||||
for (File module : file.listFiles()) {
|
||||
if (module.isDirectory() && !module.isHidden()) {
|
||||
try {
|
||||
proceedModule(file, module, project);
|
||||
} catch (JSONException e) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
project.setAppName(file.getName());
|
||||
project.setProjectPath(file.getAbsolutePath());
|
||||
if (project.getMainModule() == null
|
||||
&& project.getModulePaths().size() > 0
|
||||
&& project.getModulePaths().get(0) != null) {
|
||||
File f = new File(project.getModulePaths().get(0));
|
||||
if (!f.exists() || !f.isDirectory()) return null;
|
||||
project.setMainModule(f.getName());
|
||||
project.setMainSourcePath(new File(f, "src/main/java").getAbsolutePath());
|
||||
}
|
||||
|
||||
return project;
|
||||
}
|
||||
|
||||
private static void proceedModule(File file, File module, AndroidProject project)
|
||||
throws JSONException {
|
||||
final Pattern appPattern =
|
||||
Pattern.compile("apply\\s+plugin\\s*:\\s*(['\"])com.android.application(['\"])");
|
||||
File projectGradle = new File(file, "build.gradle");
|
||||
File moduleGradle = new File(module, "build.gradle");
|
||||
File manifest = new File(module, "src/main/AndroidManifest.xml");
|
||||
File res = new File(module, "src/main/res");
|
||||
if (!module.exists()
|
||||
|| !projectGradle.exists()
|
||||
|| !moduleGradle.exists()
|
||||
|| !manifest.exists()) return;
|
||||
if (!module.isDirectory()
|
||||
|| !moduleGradle.isFile()
|
||||
|| !projectGradle.isFile()
|
||||
|| !manifest.isFile()) return;
|
||||
|
||||
File java = new File(module, "src/main/java");
|
||||
if (java.exists() && java.isDirectory())
|
||||
project.addSource(java.getAbsolutePath()).addModule(module.getAbsolutePath());
|
||||
|
||||
File libsFolder = new File(module, "libs");
|
||||
if (libsFolder.exists() && libsFolder.isDirectory()) {
|
||||
File[] libs = libsFolder.listFiles();
|
||||
if (libs != null && libs.length > 0) {
|
||||
for (File lib : libs)
|
||||
if (lib.getName().endsWith(".jar")) project.addClasspath(lib.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
project.addSource(
|
||||
new File(module, "build/generated/source/buildConfig/debug").getAbsolutePath());
|
||||
project.addSource(
|
||||
new File(module, "build/generated/data_binding_base_class_source_out/debug/out")
|
||||
.getAbsolutePath());
|
||||
|
||||
JSONObject obj =
|
||||
new XmlToJson.Builder(FileUtil.readFile(manifest.getAbsolutePath()))
|
||||
.build()
|
||||
.toJson();
|
||||
if (!obj.has("manifest")) return;
|
||||
String iconPath = null;
|
||||
if (obj.has("manifest")
|
||||
&& obj.getJSONObject("manifest").has("application")
|
||||
&& obj.getJSONObject("manifest").getJSONObject("application").has("android:icon")) {
|
||||
String iconRes =
|
||||
obj.getJSONObject("manifest")
|
||||
.getJSONObject("application")
|
||||
.getString("android:icon");
|
||||
if (iconRes.startsWith("@drawable")) {
|
||||
final String iconName = iconRes.replace("@drawable/", "");
|
||||
File[] drawables = listDrawables(res);
|
||||
for (File drawable : drawables) {
|
||||
File[] icons = listFilesNotEndingWith(drawable, ".xml");
|
||||
for (File icon : icons) {
|
||||
String nameWithoutExtension =
|
||||
icon.getName().substring(0, icon.getName().lastIndexOf("."));
|
||||
if (nameWithoutExtension.equals(iconName)) {
|
||||
iconPath = icon.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (iconRes.startsWith("@drawable")) {
|
||||
final String iconName = iconRes.replace("@drawable/", "");
|
||||
File[] drawables = listMipmaps(res);
|
||||
for (File drawable : drawables) {
|
||||
File[] icons = listFilesNotEndingWith(drawable, ".xml");
|
||||
for (File icon : icons) {
|
||||
String nameWithoutExtension =
|
||||
icon.getName().substring(0, icon.getName().lastIndexOf("."));
|
||||
if (nameWithoutExtension.equals(iconName)) {
|
||||
iconPath = icon.getAbsolutePath();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final String packageName = obj.getJSONObject("manifest").getString("package");
|
||||
final String mainModuleName = module.getName();
|
||||
|
||||
boolean isMainModule = false;
|
||||
if (project.getMainModule() == null) {
|
||||
String content = FileIOUtils.readFile2String(moduleGradle);
|
||||
if (content != null && appPattern.matcher(content).find()) {
|
||||
isMainModule = true;
|
||||
project.setMainModule(mainModuleName);
|
||||
project.setMainSourcePath(new File(module, "src/main/java").getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
if (iconPath != null) {
|
||||
project.setIconPath(iconPath);
|
||||
}
|
||||
|
||||
if (packageName != null && isMainModule) {
|
||||
project.setPackageName(packageName);
|
||||
}
|
||||
}
|
||||
|
||||
private static File[] listDrawables(File file) {
|
||||
return listFolderStartingWith(file, "drawable");
|
||||
}
|
||||
|
||||
private static File[] listMipmaps(File file) {
|
||||
return listFolderStartingWith(file, "mipmap");
|
||||
}
|
||||
|
||||
private static File[] listFolderStartingWith(File file, final String prefix) {
|
||||
return file.listFiles(
|
||||
new FileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(File p1) {
|
||||
return p1.isDirectory() && p1.getName().startsWith(prefix);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static File[] listFilesNotEndingWith(File file, final String suffix) {
|
||||
return file.listFiles(
|
||||
new FileFilter() {
|
||||
|
||||
@Override
|
||||
public boolean accept(File p1) {
|
||||
return p1.isFile() && !p1.getName().endsWith(suffix);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -25,9 +25,6 @@ import androidx.lifecycle.MutableLiveData;
|
||||
import androidx.lifecycle.Observer;
|
||||
import androidx.lifecycle.ViewModel;
|
||||
|
||||
import com.itsaky.androidide.projects.AndroidProject;
|
||||
import com.itsaky.androidide.projects.IDEProject;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -37,8 +34,6 @@ import java.util.Objects;
|
||||
public class EditorViewModel extends ViewModel {
|
||||
|
||||
private final MutableLiveData<List<File>> mFiles = new MutableLiveData<>(new ArrayList<>());
|
||||
private final MutableLiveData<AndroidProject> mProject = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<IDEProject> mIDEProject = new MutableLiveData<>(null);
|
||||
private final MutableLiveData<Boolean> mFilesModified = new MutableLiveData<>(false);
|
||||
|
||||
/**
|
||||
@@ -47,44 +42,6 @@ public class EditorViewModel extends ViewModel {
|
||||
*/
|
||||
private final MutableLiveData<Pair<Integer, File>> mCurrentFile = new MutableLiveData<>(null);
|
||||
|
||||
/**
|
||||
* Get the current {@link AndroidProject}.
|
||||
*
|
||||
* @return The android project.
|
||||
*/
|
||||
@Nullable
|
||||
public AndroidProject getAndroidProject() {
|
||||
return mProject.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the android project currently opened in the editor activity.
|
||||
*
|
||||
* @param project The project that is opened.
|
||||
*/
|
||||
public void setAndroidProject(final AndroidProject project) {
|
||||
this.mProject.setValue(project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the {@link IDEProject} opened in the editor activity.
|
||||
*
|
||||
* @return The ide project.
|
||||
*/
|
||||
@Nullable
|
||||
public IDEProject getIDEProject() {
|
||||
return this.mIDEProject.getValue();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the IDEProject that is currently opened in the editor activity.
|
||||
*
|
||||
* @param project The project that is opened.
|
||||
*/
|
||||
public void setIDEProject(final IDEProject project) {
|
||||
this.mIDEProject.setValue(project);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the given file to the list of opened files.
|
||||
*
|
||||
@@ -112,6 +69,10 @@ public class EditorViewModel extends ViewModel {
|
||||
setCurrentFile(-1, null);
|
||||
}
|
||||
|
||||
public void setCurrentFile(final int index, @Nullable final File file) {
|
||||
mCurrentFile.setValue(Pair.create(index, file));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the opened file at the given index.
|
||||
*
|
||||
@@ -167,10 +128,6 @@ public class EditorViewModel extends ViewModel {
|
||||
return mCurrentFile.getValue().second;
|
||||
}
|
||||
|
||||
public void setCurrentFile(final int index, @Nullable final File file) {
|
||||
mCurrentFile.setValue(Pair.create(index, file));
|
||||
}
|
||||
|
||||
public void setFilesModified(boolean modified) {
|
||||
this.mFilesModified.setValue(modified);
|
||||
}
|
||||
|
||||
@@ -62,11 +62,6 @@ import com.itsaky.androidide.utils.TypefaceUtils;
|
||||
import com.itsaky.inflater.values.ValuesTableFactory;
|
||||
import com.itsaky.lsp.models.Range;
|
||||
|
||||
import io.github.rosemoe.sora.event.ContentChangeEvent;
|
||||
import io.github.rosemoe.sora.lang.EmptyLanguage;
|
||||
import io.github.rosemoe.sora.text.Content;
|
||||
import io.github.rosemoe.sora.widget.component.Magnifier;
|
||||
|
||||
import org.antlr.v4.runtime.CharStreams;
|
||||
import org.antlr.v4.runtime.Token;
|
||||
|
||||
@@ -78,6 +73,11 @@ import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
|
||||
import io.github.rosemoe.sora.event.ContentChangeEvent;
|
||||
import io.github.rosemoe.sora.lang.EmptyLanguage;
|
||||
import io.github.rosemoe.sora.text.Content;
|
||||
import io.github.rosemoe.sora.widget.component.Magnifier;
|
||||
|
||||
/**
|
||||
* A view that handles opened code editors.
|
||||
*
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user