diff --git a/.idea/misc.xml b/.idea/misc.xml index a0fea6f06..7e0ec6aee 100755 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -141,7 +141,10 @@ + + + diff --git a/app/src/main/assets/data/common/tooling-api-all.jar b/app/src/main/assets/data/common/tooling-api-all.jar index 73d4f7c4e..6e99babb4 100644 Binary files a/app/src/main/assets/data/common/tooling-api-all.jar and b/app/src/main/assets/data/common/tooling-api-all.jar differ diff --git a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java index eba83738a..466a76bc9 100755 --- a/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java +++ b/app/src/main/java/com/itsaky/androidide/fragments/preferences/BuildPreferences.java @@ -30,18 +30,24 @@ import com.blankj.utilcode.util.FileUtils; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.itsaky.androidide.R; import com.itsaky.androidide.app.BaseApplication; +import com.itsaky.androidide.databinding.LayoutDialogTextInputBinding; import com.itsaky.androidide.fragments.sheets.ProgressSheet; import com.itsaky.androidide.tasks.TaskExecutor; import com.itsaky.androidide.utils.DialogUtils; import com.itsaky.androidide.utils.Environment; +import com.itsaky.androidide.utils.ILogger; import java.io.File; +import java.util.Objects; public class BuildPreferences extends BasePreferenceFragment implements Preference.OnPreferenceClickListener, Preference.OnPreferenceChangeListener { public static final String KEY_GRADLE_COMMANDS = "idepref_build_gradleCommands"; public static final String KEY_GRADLE_CLEAR_CACHE = "idepref_build_gradleClearCache"; + public static final String KEY_CUSTOM_GRADLE_INSTALLATION = + "idepref_build_customGradleInstallation"; + private static final ILogger LOG = ILogger.newInstance("BuildPreferences"); private ProgressSheet progressSheet; @@ -52,6 +58,7 @@ public class BuildPreferences extends BasePreferenceFragment final var screen = getPreferenceScreen(); final var categoryGradle = new PreferenceCategory(getContext()); final var customCommands = new Preference(getContext()); + final var customInstallation = new Preference(getContext()); final var clearCache = new Preference(getContext()); screen.addPreference(categoryGradle); @@ -61,6 +68,11 @@ public class BuildPreferences extends BasePreferenceFragment customCommands.setTitle(R.string.idepref_build_customgradlecommands_title); customCommands.setSummary(R.string.idepref_build_customgradlecommands_summary); + customInstallation.setKey(KEY_CUSTOM_GRADLE_INSTALLATION); + customInstallation.setIcon(R.drawable.ic_gradle); + customInstallation.setTitle(getString(R.string.idepref_title_customGradleInstallation)); + customInstallation.setSummary(getString(R.string.idepref_msg_customGradleInstallation)); + clearCache.setKey(KEY_GRADLE_CLEAR_CACHE); clearCache.setIcon(R.drawable.ic_delete); clearCache.setTitle(R.string.idepref_build_clearCache_title); @@ -68,6 +80,7 @@ public class BuildPreferences extends BasePreferenceFragment categoryGradle.setTitle(R.string.gradle); categoryGradle.addPreference(customCommands); + categoryGradle.addPreference(customInstallation); categoryGradle.addPreference(clearCache); // Works only for Android 11 @@ -86,16 +99,23 @@ public class BuildPreferences extends BasePreferenceFragment setPreferenceScreen(screen); customCommands.setOnPreferenceClickListener(this); + customInstallation.setOnPreferenceClickListener(this); clearCache.setOnPreferenceClickListener(this); } @Override public boolean onPreferenceClick(Preference p1) { final String key = p1.getKey(); - if (key.equals(KEY_GRADLE_COMMANDS)) { - showGradleCommandsDialog(); - } else if (key.equals(KEY_GRADLE_CLEAR_CACHE)) { - showClearCacheDialog(); + switch (key) { + case KEY_GRADLE_COMMANDS: + showGradleCommandsDialog(); + break; + case KEY_GRADLE_CLEAR_CACHE: + showClearCacheDialog(); + break; + case KEY_CUSTOM_GRADLE_INSTALLATION: + changeGradleDist(); + break; } return true; } @@ -106,10 +126,33 @@ public class BuildPreferences extends BasePreferenceFragment if (KEY_TP_FIX.equals(key)) { getPrefManager().putBoolean(KEY_TP_FIX, ((boolean) newValue)); } - return true; } + private void changeGradleDist() { + final var builder = DialogUtils.newMaterialDialogBuilder(getContext()); + final var binding = LayoutDialogTextInputBinding.inflate(getLayoutInflater()); + binding.name.setStartIconDrawable(R.drawable.ic_gradle); + binding.name.setHint(R.string.msg_gradle_installation_path); + binding.name.setHelperText(getString(R.string.msg_gradle_installation_input_help)); + binding.name.setCounterEnabled(false); + Objects.requireNonNull(binding.name.getEditText()) + .setText(getPrefManager().getString(KEY_CUSTOM_GRADLE_INSTALLATION, "")); + builder + .setTitle(R.string.idepref_title_customGradleInstallation) + .setView(binding.getRoot()) + .setPositiveButton( + android.R.string.ok, + (dialogInterface, i) -> { + final var path = + Objects.requireNonNull(binding.name.getEditText()).getText().toString().trim(); + getPrefManager().putString(KEY_CUSTOM_GRADLE_INSTALLATION, path); + }) + .setNegativeButton( + android.R.string.cancel, (dialogInterface, i) -> dialogInterface.dismiss()) + .show(); + } + private void showGradleCommandsDialog() { final String[] labels = { "--stacktrace", diff --git a/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java b/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java index 3c75d6940..080c63968 100644 --- a/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java +++ b/app/src/main/java/com/itsaky/androidide/services/GradleBuildService.java @@ -40,11 +40,13 @@ import com.itsaky.androidide.BuildConfig; import com.itsaky.androidide.R; import com.itsaky.androidide.app.BaseApplication; import com.itsaky.androidide.app.StudioApp; +import com.itsaky.androidide.fragments.preferences.BuildPreferences; import com.itsaky.androidide.managers.PreferenceManager; import com.itsaky.androidide.models.LogLine; import com.itsaky.androidide.projects.ProjectManager; import com.itsaky.androidide.shell.CommonProcessExecutor; import com.itsaky.androidide.shell.ProcessStreamsHolder; +import com.itsaky.androidide.tooling.api.IProject; import com.itsaky.androidide.tooling.api.IToolingApiClient; import com.itsaky.androidide.tooling.api.IToolingApiServer; import com.itsaky.androidide.tooling.api.messages.InitializeProjectMessage; @@ -54,7 +56,6 @@ import com.itsaky.androidide.tooling.api.messages.result.BuildResult; import com.itsaky.androidide.tooling.api.messages.result.GradleWrapperCheckResult; import com.itsaky.androidide.tooling.api.messages.result.InitializeResult; import com.itsaky.androidide.tooling.api.messages.result.TaskExecutionResult; -import com.itsaky.androidide.tooling.api.IProject; import com.itsaky.androidide.tooling.api.util.ToolingApiLauncher; import com.itsaky.androidide.tooling.events.ProgressEvent; import com.itsaky.androidide.utils.Environment; @@ -298,7 +299,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin isBuildInProgress = true; return server - .initialize(new InitializeProjectMessage(rootDir)) + .initialize(new InitializeProjectMessage(rootDir, getGradleInstallationPath())) .thenApply(this::markBuildAsFinished); } @@ -315,10 +316,14 @@ public class GradleBuildService extends Service implements BuildService, IToolin isBuildInProgress = true; return server - .executeTasks(new TaskExecutionMessage(":", Arrays.asList(tasks))) + .executeTasks(new TaskExecutionMessage(":", Arrays.asList(tasks), getGradleInstallationPath())) .thenApply(this::markBuildAsFinished); } + private String getGradleInstallationPath() { + return StudioApp.getInstance().getPrefManager().getString(BuildPreferences.KEY_CUSTOM_GRADLE_INSTALLATION, ""); + } + @NonNull @Override public CompletableFuture executeProjectTasks( @@ -333,7 +338,7 @@ public class GradleBuildService extends Service implements BuildService, IToolin isBuildInProgress = true; return server - .executeTasks(new TaskExecutionMessage(projectPath, Arrays.asList(tasks))) + .executeTasks(new TaskExecutionMessage(projectPath, Arrays.asList(tasks), getGradleInstallationPath())) .thenApply(this::markBuildAsFinished); } @@ -393,27 +398,33 @@ public class GradleBuildService extends Service implements BuildService, IToolin return null; } - return new EventListener() { + return new EventListener() { + + @SuppressWarnings("ConstantConditions") @Override public void prepareBuild() { ThreadUtils.runOnUiThread(listener::prepareBuild); } + @SuppressWarnings("ConstantConditions") @Override public void onBuildSuccessful(@NonNull List tasks) { ThreadUtils.runOnUiThread(() -> listener.onBuildSuccessful(tasks)); } + @SuppressWarnings("ConstantConditions") @Override public void onProgressEvent(@NonNull ProgressEvent event) { ThreadUtils.runOnUiThread(() -> listener.onProgressEvent(event)); } + @SuppressWarnings("ConstantConditions") @Override public void onBuildFailed(@NonNull List tasks) { ThreadUtils.runOnUiThread(() -> listener.onBuildFailed(tasks)); } + @SuppressWarnings("ConstantConditions") @Override public void onOutput(String line) { ThreadUtils.runOnUiThread(() -> listener.onOutput(line)); diff --git a/app/src/main/res/drawable/ic_gradle.xml b/app/src/main/res/drawable/ic_gradle.xml new file mode 100644 index 000000000..b275e3b32 --- /dev/null +++ b/app/src/main/res/drawable/ic_gradle.xml @@ -0,0 +1,25 @@ + + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0404719aa..2813c9d2e 100755 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -383,4 +383,8 @@ JDK 11 tag pointers fix Set LD_PRELOAD to libhook.so to fix tag pointers issue for JDK 11 in Android 11 [DEPRECATED - Prefer using JDK 17]. + Custom Gradle installation + Specify a custom Gradle installation to use for build. This OVERRIDES the version specified in gradle-wrapper.properties. + Gradle installation path + Leave empty to use Gradle wrapper. diff --git a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt index cdf331244..2c2ebbbeb 100644 --- a/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt +++ b/build-tools/tooling-api-impl/src/main/java/com/itsaky/androidide/tooling/impl/ToolingApiServerImpl.kt @@ -43,8 +43,6 @@ import com.itsaky.androidide.tooling.impl.progress.ForwardingProgressListener import com.itsaky.androidide.tooling.impl.util.ProjectReader import com.itsaky.androidide.tooling.impl.util.StopWatch import com.itsaky.androidide.utils.ILogger -import java.io.File -import java.util.concurrent.* import org.eclipse.lsp4j.jsonrpc.CompletableFutures import org.gradle.tooling.BuildCancelledException import org.gradle.tooling.BuildException @@ -54,6 +52,8 @@ import org.gradle.tooling.GradleConnector import org.gradle.tooling.UnsupportedVersionException import org.gradle.tooling.exceptions.UnsupportedBuildArgumentException import org.gradle.tooling.exceptions.UnsupportedOperationConfigurationException +import java.io.File +import java.util.concurrent.* /** * Implementation for the Gradle Tooling API server. @@ -92,6 +92,7 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa val stopWatch = StopWatch("Connection to project") this.connector = GradleConnector.newConnector().forProjectDirectory(File(params.directory)) + setupConnectorForGradleInstallation(this.connector!!, params.gradleInstallation) stopWatch.lap("Connector created") if (this.connector == null) { @@ -153,7 +154,10 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa this.project!!.findByPath(projectPath).get() ?: return@computeAsync TaskExecutionResult(false, PROJECT_NOT_FOUND) - val connection = this.connector!!.forProjectDirectory(project.projectDir).connect() + this.connector!!.forProjectDirectory(project.projectDir) + setupConnectorForGradleInstallation(this.connector!!, message.gradleInstallation) + + val connection = this.connector!!.connect() val builder = connection.newBuild() builder.addProgressListener(ForwardingProgressListener()) @@ -183,6 +187,23 @@ internal class ToolingApiServerImpl(private val forwardingProject: InternalForwa } } + private fun setupConnectorForGradleInstallation( + connector: GradleConnector, + gradleDistribution: String? + ) { + if (gradleDistribution != null && gradleDistribution.isNotBlank()) { + val file = File(gradleDistribution) + if (file.exists() && file.isDirectory) { + log.info("Using Gradle installation:", file.canonicalPath) + connector.useInstallation(file) + } else { + log.error("Specified Gradle installation does not exist:", gradleDistribution) + } + } else { + log.info("Using Gradle wrapper for build...") + } + } + private fun notifyBuildFailure(tasks: List) { if (client != null) { client!!.onBuildFailed(BuildResult((tasks))) diff --git a/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar b/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar index 3fd94c14c..99aa6beb9 100644 Binary files a/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar and b/build-tools/tooling-api-impl/src/test/test-home/.androidide/init/model.jar differ diff --git a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt index a7200bb4b..79cc93b86 100644 --- a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt +++ b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/InitializeProjectMessage.kt @@ -22,4 +22,4 @@ package com.itsaky.androidide.tooling.api.messages * * @author Akash Yadav */ -data class InitializeProjectMessage(val directory: String) +data class InitializeProjectMessage(val directory: String, val gradleInstallation: String = "") diff --git a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt index eae89a11a..f7be93660 100644 --- a/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt +++ b/build-tools/tooling-api/src/main/java/com/itsaky/androidide/tooling/api/messages/TaskExecutionMessage.kt @@ -23,5 +23,6 @@ package com.itsaky.androidide.tooling.api.messages */ data class TaskExecutionMessage( val projectPath: String?, - val tasks: List + val tasks: List, + val gradleInstallation: String = "" )