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 = ""
)