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
13 changes: 8 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,8 @@
<img src="https://github.com/CopperLauncher/Copper-Android/workflows/Android%20CI/badge.svg" alt="Android CI"/>
<img src="https://badges.crowdin.net/pojavlauncher/localized.svg" alt="Crowdin"/>
<a href="https://discord.gg/j8GTQv3YvY"><img src="https://img.shields.io/discord/1355213558631366897?color=5865F2&logo=discord&logoColor=white&label=&style=flat" alt="Discord"></a>
<a href="https://github.com/CopperLauncher"><img src="https://img.shields.io/badge/github-CopperLauncher-orange?logo=github" alt="GitHub"></a>
<a href="https://www.curseforge.com/members/maxjubayeryt/projects"><img src="https://img.shields.io/badge/curseforge-maxjubayeryt-orange?logo=curseforge" alt="CurseForge"></a>
<a href="https://modrinth.com/user/maxjubayeryt"><img src="https://img.shields.io/badge/modrinth-maxjubayeryt-green?logo=modrinth" alt="Modrinth User"></a>
<a href="https://modrinth.com/organization/copperlauncher"><img src="https://img.shields.io/badge/modrinth-CopperLauncher-green?logo=modrinth" alt="Modrinth Org"></a>
</p>
</p>

Copper is a fork of [Amethyst](https://github.com/AngelAuraMC/Amethyst-Android) that allows you to play Minecraft: Java Edition on your Android devices.
Expand Down Expand Up @@ -89,8 +86,13 @@ If you need more control over the build process, follow these steps:
* [x] Bug Fixes
* [x] Fix GL4ES and KW in older versions
* [x] Add Modpack export
* [x] Add mclo.gs
* [ ] Add More Renders
* [x] Add mclo.gs
* [ ] Add Res pack downloader
* [ ] Add Shader pack downloader
* [ ] Add in launcher file manager
* [ ] Add Res pack manager
* [ ] Add Shader pack manager
* [ ] Add More Renerers


## Known Issues
Expand All @@ -113,6 +115,7 @@ Copper is licensed under [GNU LGPLv3](https://github.com/CopperLauncher/Copper-A
* [MojoLauncher](https://github.com/MojoLauncher/MojoLauncher/): [LGPL-3.0 license](https://github.com/AngelAuraMC/Amethyst-Android/blob/v3_openjdk/LICENSE)
* Android Support Libraries: [Apache License 2.0](https://android.googlesource.com/platform/prebuilts/maven_repo/android/+/master/NOTICE.txt).
* [GL4ES](https://github.com/AngelAuraMC/gl4es): [MIT License](https://github.com/ptitSeb/gl4es/blob/master/LICENSE).
* [Krypton Wrapper](https://github.com/BZLZHH/NG-GL4ES): [MIT License](https://github.com/BZLZHH/NG-GL4ES/blob/main/LICENSE).
* [MobileGlues](https://github.com/MobileGL-Dev/MobileGlues): [LGPL-2.1 License](https://github.com/MobileGL-Dev/MobileGlues/blob/dev-es/LICENSE).
* [ANGLE](https://chromium.googlesource.com/angle/angle): [All Rights Reserved](app_pojavlauncher/src/main/assets/licenses/ANGLE_LICENSE).
* [OpenJDK](https://github.com/AngelAuraMC/openjdk-multiarch-jdk8u): [GNU GPLv2 License](https://openjdk.java.net/legal/gplv2+ce.html).
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package net.kdt.pojavlaunch.fragments;

import android.content.Context;
import android.content.SharedPreferences;
import android.view.View;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.Spinner;
import android.widget.TextView;

import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;

import net.kdt.pojavlaunch.R;
import net.kdt.pojavlaunch.profiles.VersionSelectorDialog;

/**
* The per-instance Minecraft-version/loader filter used to drive update
* checking in "Manage Content" (mods, resource packs, and shader packs all
* share this one filter). Originally a button inside Manage Mods; now lives
* on the "Manage Content" picker sheet since it applies across all three
* content types rather than just mods.
*/
public final class ContentFilterDialog {

private static final String PREF_FILE = "mod_filters";
private static final String KEY_MC_VERSION = "mc_version_";
private static final String KEY_LOADER = "loader_";

private ContentFilterDialog() {}

public interface OnApplied {
void onApplied(String version, String loader);
}

/** Whether a version or loader filter is currently set for this profile —
* used to tint the filter icon so it's obvious a filter is active. */
public static boolean isActive(Context context, String profileKey) {
SharedPreferences prefs = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
return !prefs.getString(KEY_MC_VERSION + profileKey, "").isEmpty()
|| !prefs.getString(KEY_LOADER + profileKey, "").isEmpty();
}

public static void show(Context context, String profileKey, @Nullable OnApplied onApplied) {
SharedPreferences prefs = context.getSharedPreferences(PREF_FILE, Context.MODE_PRIVATE);
String storedVersion = prefs.getString(KEY_MC_VERSION + profileKey, "");
String storedLoader = prefs.getString(KEY_LOADER + profileKey, "");

// Nothing set yet for this instance — default to whatever version/loader
// the instance itself is actually running, rather than showing blank.
final String savedVersion;
final String savedLoader;
if (storedVersion.isEmpty() && storedLoader.isEmpty()) {
InstanceVersionResolver.Info info = InstanceVersionResolver.resolve(profileKey);
savedVersion = info.mcVersion != null ? info.mcVersion : "";
savedLoader = info.loader;
} else {
savedVersion = storedVersion;
savedLoader = storedLoader;
}

AlertDialog dialog = new AlertDialog.Builder(context)
.setView(R.layout.dialog_mod_filters)
.create();

dialog.setOnShowListener(di -> {
TextView versionText = dialog.findViewById(R.id.search_mod_selected_mc_version_textview);
Button selectVersion = dialog.findViewById(R.id.search_mod_mc_version_button);
Button applyButton = dialog.findViewById(R.id.search_mod_apply_filters);
Spinner loaderSpinner = dialog.findViewById(R.id.search_mod_loader_spinner);

// This dialog is shared with the mod/modpack search screens, which also
// show a Modrinth/CurseForge/Both engine picker — not applicable here,
// since this just filters already-installed content by version/loader
// for update checking, it doesn't pick a search engine.
View engineLabel = dialog.findViewById(R.id.search_mod_engine_textview);
View engineSpinner = dialog.findViewById(R.id.search_mod_engine_spinner);
if (engineLabel != null) engineLabel.setVisibility(View.GONE);
if (engineSpinner != null) engineSpinner.setVisibility(View.GONE);

if (versionText == null || selectVersion == null || applyButton == null) return;

versionText.setText(savedVersion);

final String[] loaderValues = { "", "fabric", "forge", "quilt", "neoforge" };
if (loaderSpinner != null) {
String[] loaderLabels = {
context.getString(R.string.search_mod_any_loader),
"Fabric", "Forge", "Quilt", "NeoForge"
};
ArrayAdapter<String> adapter = new ArrayAdapter<>(
context, android.R.layout.simple_spinner_item, loaderLabels);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
loaderSpinner.setAdapter(adapter);
for (int i = 0; i < loaderValues.length; i++) {
if (loaderValues[i].equals(savedLoader)) {
loaderSpinner.setSelection(i);
break;
}
}
}

selectVersion.setOnClickListener(v ->
VersionSelectorDialog.open(v.getContext(), true,
(id, snapshot) -> versionText.setText(id)));

applyButton.setOnClickListener(v -> {
String newVersion = versionText.getText().toString().trim();
String newLoader = (loaderSpinner != null)
? loaderValues[loaderSpinner.getSelectedItemPosition()]
: "";

prefs.edit()
.putString(KEY_MC_VERSION + profileKey, newVersion)
.putString(KEY_LOADER + profileKey, newLoader)
.apply();

if (onApplied != null) onApplied.onApplied(newVersion, newLoader);
di.dismiss();
});
});

dialog.show();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package net.kdt.pojavlaunch.fragments;

import net.kdt.pojavlaunch.JMinecraftVersionList;
import net.kdt.pojavlaunch.Tools;
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;

/**
* Infers a profile's plain Minecraft version (e.g. "1.20.1") and mod loader
* (fabric/forge/quilt/neoforge/"") from its {@code lastVersionId}. Used to
* auto-fill the Manage Content filter with sensible defaults the first time
* it's opened for an instance, instead of leaving it blank.
*/
final class InstanceVersionResolver {

private InstanceVersionResolver() {}

static final class Info {
/** May be null if it couldn't be determined at all. */
final String mcVersion;
/** "", "fabric", "forge", "quilt", or "neoforge". */
final String loader;

Info(String mcVersion, String loader) {
this.mcVersion = mcVersion;
this.loader = loader;
}
}

static Info resolve(String profileKey) {
try {
LauncherProfiles.load();
MinecraftProfile profile = LauncherProfiles.mainProfileJson.profiles.get(profileKey);
if (profile == null || profile.lastVersionId == null || profile.lastVersionId.isEmpty()) {
return new Info(null, "");
}
String versionId = profile.lastVersionId;
String loader = loaderFromVersionId(versionId);
String mcVersion = cleanMcVersion(versionId, loader);
return new Info(mcVersion, loader);
} catch (Exception e) {
return new Info(null, "");
}
}

private static String loaderFromVersionId(String versionId) {
if (versionId.startsWith("fabric-loader-")) return "fabric";
if (versionId.startsWith("quilt-loader-")) return "quilt";
if (versionId.startsWith("neoforge-")) return "neoforge";
if (versionId.contains("-forge-")) return "forge";
return "";
}

/** Strips the loader naming convention off a version id (see ModLoader.getVersionId())
* to get the plain Minecraft version underneath — e.g. "fabric-loader-0.15.11-1.20.1"
* → "1.20.1". Falls back to the version JSON's own inheritsFrom (which every mod
* loader install here sets to the vanilla version it's built on), and finally to the
* raw id itself if neither approach works (e.g. NeoForge ids don't embed the MC
* version at all, so this relies entirely on inheritsFrom for that loader). */
private static String cleanMcVersion(String versionId, String loader) {
switch (loader) {
case "fabric": {
String rest = versionId.substring("fabric-loader-".length());
int dash = rest.indexOf('-');
if (dash > 0) return rest.substring(dash + 1);
break;
}
case "quilt": {
String rest = versionId.substring("quilt-loader-".length());
int dash = rest.indexOf('-');
if (dash > 0) return rest.substring(dash + 1);
break;
}
case "forge": {
int idx = versionId.indexOf("-forge-");
if (idx > 0) return versionId.substring(0, idx);
break;
}
default:
break;
}

try {
JMinecraftVersionList.Version info = Tools.getVersionInfo(versionId);
if (info != null && info.inheritsFrom != null && !info.inheritsFrom.isEmpty()) {
return info.inheritsFrom;
}
} catch (Exception ignored) {}

return loader.isEmpty() ? versionId : null;
}
}
Loading
Loading