feat: Manual JRE downloads

Lets you decide if you wanna download the JREs in the runtime manager
This commit is contained in:
alexytomi
2026-04-04 04:01:21 +08:00
parent afaa9bda94
commit 5ce315857d
7 changed files with 128 additions and 32 deletions

View File

@@ -3,32 +3,26 @@ package net.kdt.pojavlaunch;
import static net.kdt.pojavlaunch.Architecture.archAsString;
import static net.kdt.pojavlaunch.Architecture.getDeviceArchitecture;
import static net.kdt.pojavlaunch.Tools.NATIVE_LIB_DIR;
import static net.kdt.pojavlaunch.Tools.downloadFile;
import static net.kdt.pojavlaunch.Tools.isOnline;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.util.Log;
import androidx.appcompat.app.AlertDialog;
import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
import net.kdt.pojavlaunch.multirt.Runtime;
import net.kdt.pojavlaunch.progresskeeper.DownloaderProgressWrapper;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import net.kdt.pojavlaunch.utils.JREUtils;
import net.kdt.pojavlaunch.utils.MathUtils;
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.List;
@@ -72,7 +66,7 @@ public class NewJREUtil {
}
private static MathUtils.RankedValue<Runtime> getNearestInstalledRuntime(int targetVersion) {
List<Runtime> runtimes = MultiRTUtils.getRuntimes();
List<Runtime> runtimes = MultiRTUtils.getInstalledRuntimes();
return MathUtils.findNearestPositive(targetVersion, runtimes, (runtime)->runtime.javaVersion);
}
@@ -169,8 +163,8 @@ public class NewJREUtil {
activity.getString(R.string.multirt_nocompatiblert, verInfo.javaVersion.majorVersion));
}
public static boolean isValidJavaVersion(int version) {
for (InternalRuntime javaVersion : InternalRuntime.values()) {
public static boolean isJavaVersionAvailableForDownload(int version) {
for (ExternalRuntime javaVersion : ExternalRuntime.values()) {
if (javaVersion.majorVersion == version) {
return true;
}
@@ -179,34 +173,35 @@ public class NewJREUtil {
}
private static String getJreSource(int javaVersion, String arch){
return String.format("https://github.com/AngelAuraMC/angelauramc-openjdk-build/releases/latest/download/jre%s-android-%s.tar.xz", javaVersion, arch);
return String.format("https://github.com/AngelAuraMC/angelauramc-openjdk-build/releases/download/download_jre%1$s/jre%1$s-android-%2$s.tar.xz", javaVersion, arch);
}
/**
* @return whether installation was successful or not
*/
private static boolean tryDownloadRuntime(Activity activity, int gameRequiredVersion){
if (!isOnline(activity)) return false;
private static void tryDownloadRuntime(Context activity, int javaVersion){
if (!isOnline(activity)) throw new RuntimeException(activity.getString(R.string.multirt_no_internet));
String arch = archAsString(getDeviceArchitecture());
if (!isValidJavaVersion(gameRequiredVersion)) return false;
// Checks for using this method
if (!isJavaVersionAvailableForDownload(javaVersion)) throw new RuntimeException("This is not an available JRE version");
if ((getDeviceArchitecture() == Architecture.ARCH_X86 && javaVersion >= 21)) throw new RuntimeException("x86 is not supported on Java"+javaVersion);
try {
File outputFile = new File(Tools.DIR_CACHE, String.format("jre%s-android-%s.tar.xz", gameRequiredVersion, arch));
File outputFile = new File(Tools.DIR_CACHE, String.format("jre%s-android-%s.tar.xz", javaVersion, arch));
DownloaderProgressWrapper monitor = new DownloaderProgressWrapper(R.string.newdl_downloading_jre_runtime,
ProgressLayout.UNPACK_RUNTIME);
monitor.extraString = Integer.toString(gameRequiredVersion);
monitor.extraString = Integer.toString(javaVersion);
DownloadUtils.downloadFileMonitored(
getJreSource(gameRequiredVersion, arch),
getJreSource(javaVersion, arch),
outputFile,
null,
monitor
);
String jreName = "External-" + gameRequiredVersion;
String jreName = "External-" + javaVersion;
MultiRTUtils.installRuntimeNamed(NATIVE_LIB_DIR, new FileInputStream(outputFile), jreName);
MultiRTUtils.postPrepare(jreName);
outputFile.delete();
} catch (IOException e) {
throw new RuntimeException("Failed to download Java "+gameRequiredVersion+" for "+arch, e);
throw new RuntimeException("Failed to download Java "+javaVersion+" for "+arch, e);
}
return true;
}
private enum InternalRuntime {
@@ -223,4 +218,24 @@ public class NewJREUtil {
}
}
public enum ExternalRuntime {
JRE_8(8, "External-8"),
JRE_17(17, "External-17"),
JRE_21(21, "External-21"),
JRE_25(25, "External-25");
public final int majorVersion;
public final String name;
public final String downloadLink;
public boolean isDownloading = false;
ExternalRuntime(int majorVersion, String name) {
this.majorVersion = majorVersion;
this.name = name;
this.downloadLink = getJreSource(majorVersion, archAsString(getDeviceArchitecture()));
}
public void downloadRuntime(Context activity){
tryDownloadRuntime(activity, majorVersion);
}
}
}

View File

@@ -42,7 +42,6 @@ import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class ProfileEditorFragment extends Fragment implements CropperUtils.CropperListener{
public static final String TAG = "ProfileEditorFragment";
@@ -173,7 +172,7 @@ public class ProfileEditorFragment extends Fragment implements CropperUtils.Crop
);
// Runtime spinner
List<Runtime> runtimes = MultiRTUtils.getRuntimes();
List<Runtime> runtimes = MultiRTUtils.getInstalledRuntimes();
int jvmIndex = runtimes.indexOf(new Runtime("<Default>"));
if (mTempProfile.javaDir != null) {
String selectedRuntime = mTempProfile.javaDir.substring(Tools.LAUNCHERPROFILES_RTPREFIX.length());

View File

@@ -27,11 +27,16 @@ public class MultiRTConfigDialog {
if(adapter != null) adapter.notifyDataSetChanged();
}
public MultiRTConfigDialog get() {
return this;
}
/** Build the dialog behavior and style */
public void prepare(Context activity, ActivityResultLauncher<Object> installJvmLauncher) {
mDialogView = new RecyclerView(activity);
mDialogView.setLayoutManager(new LinearLayoutManager(activity, LinearLayoutManager.VERTICAL, false));
RTRecyclerViewAdapter adapter = new RTRecyclerViewAdapter();
adapter.setDialog(get());
mDialogView.setAdapter(adapter);
mDialog = new AlertDialog.Builder(activity)

View File

@@ -1,5 +1,6 @@
package net.kdt.pojavlaunch.multirt;
import static net.kdt.pojavlaunch.Architecture.getDeviceArchitecture;
import static net.kdt.pojavlaunch.Tools.NATIVE_LIB_DIR;
import static org.apache.commons.io.FileUtils.listFiles;
@@ -8,6 +9,8 @@ import android.util.Log;
import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.Architecture;
import net.kdt.pojavlaunch.NewJREUtil.ExternalRuntime;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.utils.MathUtils;
@@ -36,7 +39,7 @@ public class MultiRTUtils {
private static final String JAVA_VERSION_STR = "JAVA_VERSION=\"";
private static final String OS_ARCH_STR = "OS_ARCH=\"";
public static List<Runtime> getRuntimes() {
public static List<Runtime> getInstalledRuntimes() {
if(!RUNTIME_FOLDER.exists() && !RUNTIME_FOLDER.mkdirs()) {
throw new RuntimeException("Failed to create runtime directory");
}
@@ -51,8 +54,25 @@ public class MultiRTUtils {
return runtimes;
}
/**
*
* @return Java versions which are not installed but are present in {@link ExternalRuntime}
*/
public static List<ExternalRuntime> getRuntimesToDownload() {
List<ExternalRuntime> runtimesToDownload = new ArrayList<>();
ExternalRuntime[] downloadableRuntimes = ExternalRuntime.values();
for (ExternalRuntime downloadableruntime : downloadableRuntimes) {
if(getExactJreName(downloadableruntime.majorVersion) == null){
// x86 isn't supported anymore for JRE25
if (!(getDeviceArchitecture() == Architecture.ARCH_X86 && downloadableruntime.majorVersion >= 21))
runtimesToDownload.add(downloadableruntime);
}
}
return runtimesToDownload;
}
public static String getExactJreName(int majorVersion) {
List<Runtime> runtimes = getRuntimes();
List<Runtime> runtimes = getInstalledRuntimes();
for(Runtime r : runtimes)
if(r.javaVersion == majorVersion)return r.name;
@@ -60,7 +80,7 @@ public class MultiRTUtils {
}
public static String getNearestJreName(int majorVersion) {
List<Runtime> runtimes = getRuntimes();
List<Runtime> runtimes = getInstalledRuntimes();
MathUtils.RankedValue<Runtime> nearestRankedRuntime = MathUtils.findNearestPositive(majorVersion, runtimes, (runtime)->runtime.javaVersion);
if(nearestRankedRuntime == null) return null;
Runtime nearestRuntime = nearestRankedRuntime.value;

View File

@@ -18,6 +18,7 @@ import androidx.appcompat.app.AlertDialog;
import androidx.recyclerview.widget.RecyclerView;
import net.kdt.pojavlaunch.Architecture;
import net.kdt.pojavlaunch.NewJREUtil;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
@@ -28,6 +29,7 @@ import java.util.List;
public class RTRecyclerViewAdapter extends RecyclerView.Adapter<RTRecyclerViewAdapter.RTViewHolder> {
private boolean mIsDeleting = false;
private MultiRTConfigDialog dialog;
@NonNull
@Override
@@ -38,13 +40,18 @@ public class RTRecyclerViewAdapter extends RecyclerView.Adapter<RTRecyclerViewAd
@Override
public void onBindViewHolder(@NonNull RTViewHolder holder, int position) {
final List<Runtime> runtimes = MultiRTUtils.getRuntimes();
holder.bindRuntime(runtimes.get(position),position);
final List<Runtime> installedRuntimes = MultiRTUtils.getInstalledRuntimes();
final List<NewJREUtil.ExternalRuntime> downloadableRuntimes = MultiRTUtils.getRuntimesToDownload();
if (installedRuntimes.size() > position) {
holder.bindInstalledRuntime(installedRuntimes.get(position),position);
} else if (installedRuntimes.size() + downloadableRuntimes.size() > position) {
holder.bindDownloadableRuntime(downloadableRuntimes.get(position - installedRuntimes.size()), position);
}
}
@Override
public int getItemCount() {
return MultiRTUtils.getRuntimes().size();
return MultiRTUtils.getInstalledRuntimes().size() + MultiRTUtils.getRuntimesToDownload().size();
}
public boolean isDefaultRuntime(Runtime rt) {
@@ -68,6 +75,10 @@ public class RTRecyclerViewAdapter extends RecyclerView.Adapter<RTRecyclerViewAd
return mIsDeleting;
}
public void setDialog(MultiRTConfigDialog multiRTConfigDialog) {
this.dialog = multiRTConfigDialog;
}
public class RTViewHolder extends RecyclerView.ViewHolder {
final TextView mJavaVersionTextView;
@@ -104,7 +115,7 @@ public class RTRecyclerViewAdapter extends RecyclerView.Adapter<RTRecyclerViewAd
mDeleteButton.setOnClickListener(v -> {
if (mCurrentRuntime == null) return;
if(MultiRTUtils.getRuntimes().size() < 2) {
if(MultiRTUtils.getInstalledRuntimes().size() < 2) {
new AlertDialog.Builder(mContext)
.setTitle(R.string.global_error)
.setMessage(R.string.multirt_config_removeerror_last)
@@ -129,7 +140,7 @@ public class RTRecyclerViewAdapter extends RecyclerView.Adapter<RTRecyclerViewAd
});
}
public void bindRuntime(Runtime runtime, int pos) {
public void bindInstalledRuntime(Runtime runtime, int pos) {
mCurrentRuntime = runtime;
mCurrentPosition = pos;
if(runtime.versionString != null && Tools.DEVICE_ARCHITECTURE == Architecture.archAsInt(runtime.arch)) {
@@ -159,6 +170,48 @@ public class RTRecyclerViewAdapter extends RecyclerView.Adapter<RTRecyclerViewAd
mSetDefaultButton.setVisibility(View.GONE);
}
@SuppressLint("NotifyDataSetChanged")
public void bindDownloadableRuntime(NewJREUtil.ExternalRuntime runtime, int pos) {
mCurrentPosition = pos;
mJavaVersionTextView.setText(runtime.name
.replace(".tar.xz", "")
.replace("-", " "));
mFullJavaVersionTextView.setText(R.string.global_not_installed);
mFullJavaVersionTextView.setTextColor(mDefaultColors);
mSetDefaultButton.setVisibility(View.VISIBLE);
mDeleteButton.setVisibility(View.GONE);
if (runtime.isDownloading) {
mSetDefaultButton.setEnabled(false);
mSetDefaultButton.setText(R.string.global_installing);
} else {
mSetDefaultButton.setEnabled(true);
mSetDefaultButton.setText(R.string.global_download);
}
mSetDefaultButton.setOnClickListener(v -> {
runtime.isDownloading = true;
mSetDefaultButton.setEnabled(false);
mSetDefaultButton.setText(R.string.global_download);
sExecutorService.execute(() -> {
mSetDefaultButton.setText(R.string.global_installing);
try {
runtime.downloadRuntime(v.getContext());
} catch (RuntimeException e) {
Tools.showErrorRemote(e);
}
v.post(() -> {
// Reset the listener for this button so SET DEFAULT actually sets default
setupOnClickListeners();
// Update the UI so it knows it got installed
notifyDataSetChanged();
runtime.isDownloading = false;
});
});
});
}
private void updateButtonsVisibility(){
mSetDefaultButton.setVisibility(mIsDeleting ? View.GONE : View.VISIBLE);
mDeleteButton.setVisibility(mIsDeleting ? View.VISIBLE : View.GONE);

View File

@@ -133,11 +133,11 @@ public class LauncherPreferences {
if(DEFAULT_PREF.contains("defaultRuntime")) {
PREF_DEFAULT_RUNTIME = DEFAULT_PREF.getString("defaultRuntime","");
}else{
if(MultiRTUtils.getRuntimes().isEmpty()) {
if(MultiRTUtils.getInstalledRuntimes().isEmpty()) {
PREF_DEFAULT_RUNTIME = "";
return;
}
PREF_DEFAULT_RUNTIME = MultiRTUtils.getRuntimes().get(0).name;
PREF_DEFAULT_RUNTIME = MultiRTUtils.getInstalledRuntimes().get(0).name;
LauncherPreferences.DEFAULT_PREF.edit().putString("defaultRuntime",LauncherPreferences.PREF_DEFAULT_RUNTIME).apply();
}
}

View File

@@ -502,5 +502,9 @@
Sodium is unsupported, you are on your own. No support will be given in the discord server.\nUsing sodium may result in bugs, glitches, and crashes. No help will be given even if you lose any of your worlds or saves.\n\nNow what\'s %1$s multiplied by %2$s plus %3$s minus %4$s?
</string>
<string name="not_modpack_file">Not a modpack file!</string>
<string name="global_not_installed">Not Installed</string>
<string name="global_download">Download</string>
<string name="multirt_no_internet">Failed to download, check your internet connection.</string>
<string name="global_installing">Installing…</string>
</resources>