qol[ProfileType]: Add NeoForge install button

I don't know how it manages to make a profile but it does
This commit is contained in:
alexytomi 2025-06-19 00:49:45 +08:00
parent c32f459caf
commit 3d7cf8b3ad
6 changed files with 300 additions and 0 deletions

View File

@ -0,0 +1,80 @@
package net.kdt.pojavlaunch.fragments;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.widget.ExpandableListAdapter;
import androidx.annotation.NonNull;
import net.kdt.pojavlaunch.JavaGUILauncherActivity;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.modloaders.ForgeDownloadTask;
import net.kdt.pojavlaunch.modloaders.ForgeUtils;
import net.kdt.pojavlaunch.modloaders.ModloaderListenerProxy;
import net.kdt.pojavlaunch.modloaders.NeoForgeDownloadTask;
import net.kdt.pojavlaunch.modloaders.NeoForgeVersionListAdapter;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.List;
public class NeoForgeInstallFragment extends ModVersionListFragment<List<String>> {
public static final String TAG = "NeoForgeInstallFragment";
public NeoForgeInstallFragment() {
super(TAG);
}
private static final String NEOFORGE_METADATA_URL = "https://meta.prismlauncher.org/v1/net.neoforged/index.json";
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
}
@Override
public int getTitleText() {
return R.string.neoforge_dl_select_version;
}
@Override
public int getNoDataMsg() {
return R.string.neoforge_dl_no_installer;
}
@Override
public List<String> loadVersionList() {
String test = null;
try {
test = DownloadUtils.downloadStringCached(NEOFORGE_METADATA_URL, "neoforge_versions", input -> input);
} catch (Exception e) {
Tools.showErrorRemote(e);
}
return Collections.singletonList(test);
// Moved the parsing logic to the adapter because there is no way to get this info easily, we use prism's index
// since neoforge doesn't actually give this information easily anywhere.
// To clarify, neoforge does not provide maven APIs to get supported Minecraft versions for each loader version
}
@Override
public ExpandableListAdapter createAdapter(List<String> versionList, LayoutInflater layoutInflater) {
return new NeoForgeVersionListAdapter(versionList, layoutInflater);
}
@Override
public Runnable createDownloadTask(Object selectedVersion, ModloaderListenerProxy listenerProxy) {
return new NeoForgeDownloadTask(listenerProxy, (String) selectedVersion);
}
@Override
public void onDownloadFinished(Context context, File downloadedFile) {
Intent modInstallerStartIntent = new Intent(context, JavaGUILauncherActivity.class);
modInstallerStartIntent.putExtra("javaArgs", "-jar "+downloadedFile.getAbsolutePath()+" --install-client");
context.startActivity(modInstallerStartIntent);
}
}

View File

@ -36,6 +36,8 @@ public class ProfileTypeSelectFragment extends Fragment {
tryInstall(FabricInstallFragment.class, FabricInstallFragment.TAG));
view.findViewById(R.id.modded_profile_forge).setOnClickListener((v)->
tryInstall(ForgeInstallFragment.class, ForgeInstallFragment.TAG));
view.findViewById(R.id.modded_profile_neoforge).setOnClickListener((v)->
tryInstall(NeoForgeInstallFragment.class, NeoForgeInstallFragment.TAG));
view.findViewById(R.id.modded_profile_modpack).setOnClickListener((v)->
tryInstall(SearchModFragment.class, SearchModFragment.TAG));
view.findViewById(R.id.modded_profile_quilt).setOnClickListener((v)->

View File

@ -0,0 +1,88 @@
package net.kdt.pojavlaunch.modloaders;
import com.kdt.mcgui.ProgressLayout;
import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.progresskeeper.ProgressKeeper;
import net.kdt.pojavlaunch.utils.DownloadUtils;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.List;
public class NeoForgeDownloadTask implements Runnable, Tools.DownloaderFeedback {
private String mDownloadUrl;
private String mFullVersion;
private String mLoaderVersion;
private String mGameVersion;
private final ModloaderDownloadListener mListener;
public NeoForgeDownloadTask(ModloaderDownloadListener listener, String forgeVersion) {
this.mListener = listener;
this.mDownloadUrl = "https://maven.neoforged.net/releases/net/neoforged/neoforge/"+ forgeVersion +"/neoforge-"+forgeVersion+"-installer.jar";
this.mFullVersion = forgeVersion;
}
public NeoForgeDownloadTask(ModloaderDownloadListener listener, String gameVersion, String loaderVersion) {
this.mListener = listener;
this.mLoaderVersion = loaderVersion;
this.mGameVersion = gameVersion;
}
@Override
public void run() {
if(determineDownloadUrl()) {
downloadForge();
}
ProgressLayout.clearProgress(ProgressLayout.INSTALL_MODPACK);
}
@Override
public void updateProgress(int curr, int max) {
int progress100 = (int)(((float)curr / (float)max)*100f);
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, progress100, R.string.forge_dl_progress, mFullVersion);
}
private void downloadForge() {
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.forge_dl_progress, mFullVersion);
try {
File destinationFile = new File(Tools.DIR_CACHE, "neoforge-installer.jar");
byte[] buffer = new byte[8192];
DownloadUtils.downloadFileMonitored(mDownloadUrl, destinationFile, buffer, this);
mListener.onDownloadFinished(destinationFile);
}catch (FileNotFoundException e) {
mListener.onDataNotAvailable();
} catch (IOException e) {
mListener.onDownloadError(e);
}
}
public boolean determineDownloadUrl() {
if(mDownloadUrl != null && mFullVersion != null) return true;
ProgressKeeper.submitProgress(ProgressLayout.INSTALL_MODPACK, 0, R.string.forge_dl_searching);
try {
if(!findVersion()) {
mListener.onDataNotAvailable();
return false;
}
}catch (IOException e) {
mListener.onDownloadError(e);
return false;
}
return true;
}
public boolean findVersion() throws IOException {
List<String> forgeVersions = ForgeUtils.downloadForgeVersions();
if(forgeVersions == null) return false;
String versionStart = mGameVersion+"-"+mLoaderVersion;
for(String versionName : forgeVersions) {
if(!versionName.startsWith(versionStart)) continue;
mFullVersion = versionName;
mDownloadUrl = ForgeUtils.getInstallerUrl(mFullVersion);
return true;
}
return false;
}
}

View File

@ -0,0 +1,119 @@
package net.kdt.pojavlaunch.modloaders;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListAdapter;
import android.widget.TextView;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.List;
public class NeoForgeVersionListAdapter extends BaseExpandableListAdapter implements ExpandableListAdapter {
private final LayoutInflater mLayoutInflater;
private final LinkedHashMap<String, LinkedHashSet<String>> minecraftToLoaderVersionsHashmap;
private LinkedHashSet<String> generatedHashSet = null;
public NeoForgeVersionListAdapter(List<String> forgeVersions, LayoutInflater layoutInflater) {
this.mLayoutInflater = layoutInflater;
minecraftToLoaderVersionsHashmap = new LinkedHashMap<>();
JsonArray versionsJsonArray = JsonParser.parseString(forgeVersions.get(0)).getAsJsonObject().getAsJsonArray("versions");
ArrayList<JsonElement> sortedVersionsList = new ArrayList<>();
for (JsonElement elem : versionsJsonArray) {
sortedVersionsList.add(elem);
}
Collections.sort(sortedVersionsList, (o1, o2) -> {
String versionString1 = ((JsonObject) o1).get("requires").getAsJsonArray().get(0).getAsJsonObject().get("equals").getAsString();
String versionString2 = ((JsonObject) o2).get("requires").getAsJsonArray().get(0).getAsJsonObject().get("equals").getAsString();
return versionString2.compareTo(versionString1); // Sorts by Minecraft version
});
for (JsonElement sortedVersionPick : sortedVersionsList) {
String loaderVersion = ((JsonObject) sortedVersionPick).get("version").getAsString();
String minecraftVersion = ((JsonObject) sortedVersionPick).get("requires").getAsJsonArray().get(0).getAsJsonObject().get("equals").getAsString();
if (minecraftToLoaderVersionsHashmap.containsKey(minecraftVersion)) {
minecraftToLoaderVersionsHashmap.get(minecraftVersion).add(loaderVersion);
} else {
generatedHashSet = new LinkedHashSet<>();
generatedHashSet.add(loaderVersion);
minecraftToLoaderVersionsHashmap.put(minecraftVersion, generatedHashSet);
}
}
}
@Override
public int getGroupCount() {
return minecraftToLoaderVersionsHashmap.size();
}
@Override
public int getChildrenCount(int i) {
return new ArrayList<>(minecraftToLoaderVersionsHashmap.values()).get(i).size();
}
@Override
public Object getGroup(int i) {
return getGameVersion(i);
}
@Override
public Object getChild(int i, int i1) {
return getForgeVersion(i, i1);
}
@Override
public long getGroupId(int i) {
return i;
}
@Override
public long getChildId(int i, int i1) {
return i1;
}
@Override
public boolean hasStableIds() {
return true;
}
@Override
public View getGroupView(int i, boolean b, View convertView, ViewGroup viewGroup) {
if(convertView == null)
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
((TextView) convertView).setText(getGameVersion(i));
return convertView;
}
@Override
public View getChildView(int i, int i1, boolean b, View convertView, ViewGroup viewGroup) {
if(convertView == null)
convertView = mLayoutInflater.inflate(android.R.layout.simple_expandable_list_item_1, viewGroup, false);
((TextView) convertView).setText(getForgeVersion(i, i1));
return convertView;
}
private String getGameVersion(int i) {
return minecraftToLoaderVersionsHashmap.keySet().toArray()[i].toString();
}
private String getForgeVersion(int i, int i1){
return new ArrayList<>(minecraftToLoaderVersionsHashmap.values()).get(i).toArray()[i1].toString();
}
@Override
public boolean isChildSelectable(int i, int i1) {
return true;
}
}

View File

@ -103,6 +103,14 @@
android:layout_marginTop="@dimen/padding_large"
android:text="@string/modloader_dl_install_forge" />
<com.kdt.mcgui.MineButton
android:id="@+id/modded_profile_neoforge"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/padding_large"
android:layout_marginTop="@dimen/padding_large"
android:text="@string/modloader_dl_install_neoforge" />
<com.kdt.mcgui.MineButton
android:id="@+id/modded_profile_modpack"
android:layout_width="match_parent"

View File

@ -450,4 +450,7 @@
<string name="change_account">Please change accounts to use this function</string>
<string name="no_minecraft_account_found">No Minecraft Account Found</string>
<string name="feature_requires_java_account">This feature requires a Microsoft account that owns Minecraft Java Edition.</string>
<string name="modloader_dl_install_neoforge">Create Neoforge profile</string>
<string name="neoforge_dl_select_version">Select NeoForge version</string>
<string name="neoforge_dl_no_installer">Sorry, but this version of NeoForge does not have an installer, which is not yet supported.</string>
</resources>