Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ jobs:
rm -rf app_pojavlauncher/src/main/assets/components/jre
rm -rf app_pojavlauncher/src/main/assets/components/jre-new
rm -rf app_pojavlauncher/src/main/assets/components/jre-21
rm -rf app_pojavlauncher/src/main/assets/components/jre-25
./gradlew assembleDebug
mv app_pojavlauncher/build/outputs/apk/debug/app_pojavlauncher-debug.apk out/app-debug-noruntime.apk

Expand Down
14 changes: 2 additions & 12 deletions app_pojavlauncher/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ static def getDate() { return new Date().format('yyyyMMdd') }
def getVersionName = {
// Get the last version tag, as well as the short head of the last commit
ByteArrayOutputStream TAG = new ByteArrayOutputStream()
ByteArrayOutputStream BRANCH = new ByteArrayOutputStream()
String semVer = "1.0.0"
// Used by the fallback for github actions
ByteArrayOutputStream TAG_PART_COMMIT = new ByteArrayOutputStream()
String TAG_STRING
Expand Down Expand Up @@ -48,17 +48,7 @@ def getVersionName = {

}

exec {
try {
commandLine 'git', 'branch', '--show-current'
ignoreExitValue true
standardOutput = BRANCH
} catch (Exception e) {
e.printStackTrace();
}
}

return TAG_STRING.trim().replace("-g", "-") + "-" + BRANCH.toString().trim()
return TAG_STRING.trim().replace("-g", "-") + "-" + semVer
}

def getCFApiKey = {
Expand Down
Binary file modified app_pojavlauncher/libs/MobileGlues-release.aar
Binary file not shown.
Binary file not shown.

This file was deleted.

37 changes: 35 additions & 2 deletions app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Tools.java
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ private static File getGameDir() {

/**
* Searches for mod in mods directory of current selected profile
* Not case-sensitive
* @param filenames Filename(s) of the .jar mod(s)
* @return Whether or not the .jar is found
*/
Expand All @@ -278,7 +279,7 @@ public static boolean hasMods(String... filenames) {
if (modFiles == null) return false;
for (File file : modFiles) {
for (String filename : filenames)
if (file.getName().contains(filename)) return true;
if (file.getName().toLowerCase().contains(filename.toLowerCase())) return true;
}
return false;
}
Expand Down Expand Up @@ -320,10 +321,12 @@ private static boolean affectedByRenderDistanceIssue() {
return info.isAdreno() && info.glesMajorVersion >= 3;
}

private static String[] sodiumMods = {"sodium", "embeddium", "rubidium", "xenon"};

private static boolean affectedByLTWRenderDistanceIssue() {
if(!"opengles3_ltw".equals(Tools.LOCAL_RENDERER)) return false;
if(!affectedByRenderDistanceIssue()) return false;
if(hasMods("sodium", "embeddium", "rubidium")) return false;
if(hasMods(sodiumMods)) return false;

int renderDistance;
try {
Expand Down Expand Up @@ -432,7 +435,37 @@ public static void launchMinecraft(final AppCompatActivity activity, MinecraftAc
javaArgList.add("-Dimgui.library.name=imgui-java");
// We use an abomination to support all DH versions with a single library.
javaArgList.add("-DZstdNativePath="+Tools.NATIVE_LIB_DIR+"/libzstd-jni-1.5.7-6-dhcompat.so");
// We only ever reach this point when user has already used the force run switch
boolean hasSodiumMod = false;
for (String modName : sodiumMods) {
if (hasMods(sodiumMods)) {
hasSodiumMod = true;
File mixinPropertiesConfigFile = new File(getGameDir(), "config/" + modName + "-mixins.properties");
// Write mixin configs to somewhat help stability. We don't want more people complaining.
String[] propertiesToAdd = {
"mixin.features.buffer_builder.intrinsics=false",
"mixin.features.chunk_rendering=false"
};
List<String> mixinPropertiesConfigStrings = null;
try {
mixinPropertiesConfigStrings = org.apache.commons.io.FileUtils.readLines(mixinPropertiesConfigFile, "UTF-8");
} catch (IOException ignored) {}
if (mixinPropertiesConfigStrings == null) {
mixinPropertiesConfigStrings = new ArrayList<>();
}
for (String newLine : propertiesToAdd) {
if (!mixinPropertiesConfigStrings.contains(newLine)) {
mixinPropertiesConfigStrings.add(newLine);
}
}
try {
org.apache.commons.io.FileUtils.writeLines(mixinPropertiesConfigFile, mixinPropertiesConfigStrings);
} catch (IOException ignored) {} // If we can't write it, we tried our best.

}
}
// We use a janky lwjgl setup. We don't want more people complaining it crashes.
if (hasSodiumMod) javaArgList.add("-Dsodium.checks.issue2561=false");
javaArgList.add(versionInfo.mainClass);
javaArgList.addAll(Arrays.asList(launchArgs));
// ctx.appendlnToLog("full args: "+javaArgList.toString());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
package net.kdt.pojavlaunch.fragments;

import static net.kdt.pojavlaunch.Tools.dialogOnUiThread;
import static net.kdt.pojavlaunch.Tools.hasNoOnlineProfileDialog;
import static net.kdt.pojavlaunch.Tools.hasOnlineProfile;
import static net.kdt.pojavlaunch.Tools.openPath;
import static net.kdt.pojavlaunch.Tools.runOnUiThread;
import static net.kdt.pojavlaunch.Tools.shareLog;

import android.app.Dialog;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
Expand All @@ -17,7 +14,6 @@

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;

import com.kdt.mcgui.mcVersionSpinner;
Expand All @@ -39,63 +35,93 @@ public class MainMenuFragment extends Fragment {

private mcVersionSpinner mVersionSpinner;

public MainMenuFragment(){
public MainMenuFragment() {
super(R.layout.fragment_launcher);
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
Button mNewsButton = view.findViewById(R.id.news_button);
Button mDiscordButton = view.findViewById(R.id.discord_button);
Button mCustomControlButton = view.findViewById(R.id.custom_control_button);
Button mInstallJarButton = view.findViewById(R.id.install_jar_button);
Button mShareLogsButton = view.findViewById(R.id.share_logs_button);
Button mOpenDirectoryButton = view.findViewById(R.id.open_files_button);
Button mNewsButton = view.findViewById(R.id.news_button);
Button mDiscordButton = view.findViewById(R.id.discord_button);
Button mCustomControlButton = view.findViewById(R.id.custom_control_button);
Button mInstallJarButton = view.findViewById(R.id.install_jar_button);
Button mShareLogsButton = view.findViewById(R.id.share_logs_button);
Button mManageModsButton = view.findViewById(R.id.open_files_button);
Button mOpenDirectoryButton = view.findViewById(R.id.open_directory_button);
Button mModStoreButton = view.findViewById(R.id.mod_store_button);

ImageButton mEditProfileButton = view.findViewById(R.id.edit_profile_button);
Button mPlayButton = view.findViewById(R.id.play_button);
mVersionSpinner = view.findViewById(R.id.mc_version_spinner);

// Wiki
mNewsButton.setOnClickListener(v -> Tools.openURL(requireActivity(), Tools.URL_HOME));

// Discord
mDiscordButton.setOnClickListener(v -> Tools.openURL(requireActivity(), getString(R.string.discord_invite)));
mCustomControlButton.setOnClickListener(v -> startActivity(new Intent(requireContext(), CustomControlsActivity.class)));

// Custom controls
mCustomControlButton.setOnClickListener(v ->
startActivity(new Intent(requireContext(), CustomControlsActivity.class)));

// Mod Store → individual mod search
if (mModStoreButton != null)
mModStoreButton.setOnClickListener(v ->
Tools.swapFragment(requireActivity(), ModsSearchFragment.class, ModsSearchFragment.TAG, null));

// Execute .jar
if (hasOnlineProfile()) {
mInstallJarButton.setOnClickListener(v -> runInstallerWithConfirmation(false));
mInstallJarButton.setOnLongClickListener(v -> {
runInstallerWithConfirmation(true);
return true;
});
} else mInstallJarButton.setOnClickListener(v -> hasNoOnlineProfileDialog(requireActivity()));
mEditProfileButton.setOnClickListener(v -> mVersionSpinner.openProfileEditor(requireActivity()));

mPlayButton.setOnClickListener(v -> {
ExtraCore.setValue(ExtraConstants.LAUNCH_GAME, true);
});

mShareLogsButton.setOnClickListener((v) -> shareLog(requireContext()));

mOpenDirectoryButton.setOnClickListener((v)-> {
if (Tools.isDemoProfile(v.getContext())){ // Say a different message when on demo profile since they might see the hidden demo folder
hasNoOnlineProfileDialog(getActivity(), getString(R.string.demo_unsupported), getString(R.string.change_account));
} else if (!hasOnlineProfile()) { // Otherwise display the generic pop-up to log in
hasNoOnlineProfileDialog(requireActivity());
} else openPath(v.getContext(), getCurrentProfileDirectory(), false);
} else {
mInstallJarButton.setOnClickListener(v -> hasNoOnlineProfileDialog(requireActivity()));
}

// Share logs
if (mShareLogsButton != null)
mShareLogsButton.setOnClickListener(v -> shareLog(requireContext()));

// Manage Mods & Tools → new UI
mManageModsButton.setOnClickListener(v ->
Tools.swapFragment(requireActivity(), ManageModsFragment.class, ManageModsFragment.TAG, null));

// Open game directory (original behaviour)
if (mOpenDirectoryButton != null) {
mOpenDirectoryButton.setOnClickListener(v -> {
if (Tools.isDemoProfile(v.getContext())) {
hasNoOnlineProfileDialog(getActivity(),
getString(R.string.demo_unsupported), getString(R.string.change_account));
} else if (!hasOnlineProfile()) {
hasNoOnlineProfileDialog(requireActivity());
} else {
openPath(v.getContext(), getCurrentProfileDirectory(), false);
}
});
}

});
// Edit profile
mEditProfileButton.setOnClickListener(v -> mVersionSpinner.openProfileEditor(requireActivity()));

// Play
mPlayButton.setOnClickListener(v -> ExtraCore.setValue(ExtraConstants.LAUNCH_GAME, true));

mNewsButton.setOnLongClickListener((v)->{
// Long-press wiki → gamepad mapper (hidden)
mNewsButton.setOnLongClickListener(v -> {
Tools.swapFragment(requireActivity(), GamepadMapperFragment.class, GamepadMapperFragment.TAG, null);
return true;
});
}

private File getCurrentProfileDirectory() {
String currentProfile = LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE, null);
if(!Tools.isValidString(currentProfile)) return new File(Tools.DIR_GAME_NEW);
String currentProfile = LauncherPreferences.DEFAULT_PREF
.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE, null);
if (!Tools.isValidString(currentProfile)) return new File(Tools.DIR_GAME_NEW);
LauncherProfiles.load();
MinecraftProfile profileObject = LauncherProfiles.mainProfileJson.profiles.get(currentProfile);
if(profileObject == null) return new File(Tools.DIR_GAME_NEW);
if (profileObject == null) return new File(Tools.DIR_GAME_NEW);
return Tools.getGameDirPath(profileObject);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package net.kdt.pojavlaunch.fragments;

import android.os.Bundle;
import android.view.View;
import android.widget.ImageButton;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.fragments.ModsSearchFragment;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.modloaders.InstalledModAdapter;
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;

import java.io.File;

public class ManageModsFragment extends Fragment {

public static final String TAG = "ManageModsFragment";

public ManageModsFragment() {
super(R.layout.fragment_manage_mods);
}

@Override
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
ImageButton backButton = view.findViewById(R.id.manage_mods_back);
ImageButton addButton = view.findViewById(R.id.manage_mods_add);
TextView title = view.findViewById(R.id.manage_mods_title);
RecyclerView recycler = view.findViewById(R.id.manage_mods_recycler);
View emptyState = view.findViewById(R.id.manage_mods_empty);

// Back
backButton.setOnClickListener(v -> Tools.removeCurrentFragment(requireActivity()));

// Add → open mod store
addButton.setOnClickListener(v ->
Tools.swapFragment(requireActivity(), ModsSearchFragment.class, ModsSearchFragment.TAG, null));

// Title: "ProfileName - Mods"
String profileName = getCurrentProfileName();
title.setText(profileName.isEmpty()
? getString(R.string.mcl_button_manage_mods)
: profileName + " - Mods");

// Build mod list
File modsDir = getModsDir();
InstalledModAdapter adapter = new InstalledModAdapter(modsDir, isEmpty -> {
recycler.setVisibility(isEmpty ? View.GONE : View.VISIBLE);
emptyState.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
});

recycler.setLayoutManager(new LinearLayoutManager(requireContext()));
recycler.setAdapter(adapter);
}

// ── Helpers ─────────────────────────────────────────────────────────────

private String getCurrentProfileName() {
try {
String key = LauncherPreferences.DEFAULT_PREF
.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE, null);
if (key == null || key.isEmpty()) return "";
LauncherProfiles.load();
MinecraftProfile profile = LauncherProfiles.mainProfileJson.profiles.get(key);
if (profile == null) return "";
return profile.name != null ? profile.name : key;
} catch (Exception e) {
return "";
}
}

private File getModsDir() {
try {
String key = LauncherPreferences.DEFAULT_PREF
.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE, null);
if (key != null && !key.isEmpty()) {
LauncherProfiles.load();
MinecraftProfile profile = LauncherProfiles.mainProfileJson.profiles.get(key);
if (profile != null) {
File gameDir = Tools.getGameDirPath(profile);
return new File(gameDir, "mods");
}
}
} catch (Exception ignored) {}
return new File(Tools.DIR_GAME_NEW, "mods");
}
}
Loading
Loading