mirror of
https://github.com/2009scape/2009Scape-mobile.git
synced 2025-12-09 16:45:37 -07:00
1037 lines
45 KiB
Java
1037 lines
45 KiB
Java
package net.kdt.pojavlaunch;
|
|
|
|
import static android.os.Build.VERSION.SDK_INT;
|
|
import static android.os.Build.VERSION_CODES.P;
|
|
import static net.kdt.pojavlaunch.PojavApplication.sExecutorService;
|
|
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_IGNORE_NOTCH;
|
|
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE;
|
|
|
|
import android.app.Activity;
|
|
import android.app.ActivityManager;
|
|
import android.app.AlertDialog;
|
|
import android.app.NotificationChannel;
|
|
import android.app.NotificationManager;
|
|
import android.app.ProgressDialog;
|
|
import android.content.ClipData;
|
|
import android.content.ClipboardManager;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.res.Configuration;
|
|
import android.database.Cursor;
|
|
import android.net.Uri;
|
|
import android.os.Build;
|
|
import android.os.Bundle;
|
|
import android.os.Environment;
|
|
import android.os.Handler;
|
|
import android.os.Looper;
|
|
import android.provider.DocumentsContract;
|
|
import android.provider.OpenableColumns;
|
|
import android.util.ArrayMap;
|
|
import android.util.DisplayMetrics;
|
|
import android.util.Log;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.webkit.MimeTypeMap;
|
|
import android.widget.EditText;
|
|
import android.widget.Toast;
|
|
|
|
import androidx.annotation.NonNull;
|
|
import androidx.annotation.Nullable;
|
|
import androidx.core.app.NotificationManagerCompat;
|
|
import androidx.fragment.app.Fragment;
|
|
import androidx.fragment.app.FragmentActivity;
|
|
import androidx.fragment.app.FragmentTransaction;
|
|
|
|
import com.google.gson.Gson;
|
|
import com.google.gson.GsonBuilder;
|
|
|
|
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
|
import net.kdt.pojavlaunch.extra.ExtraCore;
|
|
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
|
|
import net.kdt.pojavlaunch.multirt.Runtime;
|
|
import net.kdt.pojavlaunch.plugins.FFmpegPlugin;
|
|
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
|
import net.kdt.pojavlaunch.utils.DownloadUtils;
|
|
import net.kdt.pojavlaunch.utils.JREUtils;
|
|
import net.kdt.pojavlaunch.utils.JSONUtils;
|
|
import net.kdt.pojavlaunch.utils.OldVersionsUtils;
|
|
import net.kdt.pojavlaunch.value.DependentLibrary;
|
|
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
|
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
|
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
|
|
|
import org.apache.commons.codec.binary.Hex;
|
|
import org.apache.commons.io.IOUtils;
|
|
import org.lwjgl.glfw.CallbackBridge;
|
|
|
|
import java.io.BufferedInputStream;
|
|
import java.io.BufferedOutputStream;
|
|
import java.io.File;
|
|
import java.io.FileInputStream;
|
|
import java.io.FileNotFoundException;
|
|
import java.io.FileOutputStream;
|
|
import java.io.IOException;
|
|
import java.io.InputStream;
|
|
import java.io.OutputStream;
|
|
import java.io.PrintWriter;
|
|
import java.io.StringWriter;
|
|
import java.lang.reflect.Field;
|
|
import java.nio.charset.StandardCharsets;
|
|
import java.util.ArrayList;
|
|
import java.util.Arrays;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.zip.ZipEntry;
|
|
import java.util.zip.ZipInputStream;
|
|
import java.util.zip.ZipOutputStream;
|
|
|
|
@SuppressWarnings("IOStreamConstructor")
|
|
public final class Tools {
|
|
public static final Handler MAIN_HANDLER = new Handler(Looper.getMainLooper());
|
|
public static String APP_NAME = "null";
|
|
|
|
|
|
public static final String RT4_MAIN_CLASS = "rt4.client";
|
|
public static final Gson GLOBAL_GSON = new GsonBuilder().setPrettyPrinting().create();
|
|
|
|
public static final String URL_HOME = "https://pojavlauncherteam.github.io";
|
|
public static String NATIVE_LIB_DIR;
|
|
public static String DIR_DATA; //Initialized later to get context
|
|
public static File DIR_CACHE;
|
|
public static String MULTIRT_HOME;
|
|
public static String LOCAL_RENDERER = null;
|
|
public static int DEVICE_ARCHITECTURE;
|
|
public static final String LAUNCHERPROFILES_RTPREFIX = "pojav://";
|
|
|
|
// New since 3.3.1
|
|
public static String DIR_ACCOUNT_NEW;
|
|
public static String DIR_GAME_HOME = Environment.getExternalStorageDirectory().getAbsolutePath() + "/games/PojavLauncher";
|
|
public static String DIR_GAME_NEW;
|
|
|
|
// New since 3.0.0
|
|
public static String DIRNAME_HOME_JRE = "lib";
|
|
|
|
// New since 2.4.2
|
|
public static String DIR_HOME_VERSION;
|
|
public static String DIR_HOME_LIBRARY;
|
|
|
|
public static String DIR_HOME_CRASH;
|
|
|
|
public static String ASSETS_PATH;
|
|
public static String OBSOLETE_RESOURCES_PATH;
|
|
public static String CTRLMAP_PATH;
|
|
public static String CTRLDEF_FILE;
|
|
public static final int RUN_MOD_INSTALLER = 2050;
|
|
|
|
|
|
private static File getPojavStorageRoot(Context ctx) {
|
|
if(SDK_INT >= 29) {
|
|
return ctx.getExternalFilesDir(null);
|
|
}else{
|
|
return new File(Environment.getExternalStorageDirectory(),"games/PojavLauncher");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Checks if the Pojav's storage root is accessible and read-writable
|
|
* @param context context to get the storage root if it's not set yet
|
|
* @return true if storage is fine, false if storage is not accessible
|
|
*/
|
|
public static boolean checkStorageRoot(Context context) {
|
|
File externalFilesDir = DIR_GAME_HOME == null ? Tools.getPojavStorageRoot(context) : new File(DIR_GAME_HOME);
|
|
//externalFilesDir == null when the storage is not mounted if it was obtained with the context call
|
|
return externalFilesDir != null && Environment.getExternalStorageState(externalFilesDir).equals(Environment.MEDIA_MOUNTED);
|
|
}
|
|
|
|
/**
|
|
* Since some constant requires the use of the Context object
|
|
* You can call this function to initialize them.
|
|
* Any value (in)directly dependant on DIR_DATA should be set only here.
|
|
*/
|
|
public static void initContextConstants(Context ctx){
|
|
DIR_CACHE = ctx.getCacheDir();
|
|
DIR_DATA = ctx.getFilesDir().getParent();
|
|
MULTIRT_HOME = DIR_DATA+"/runtimes";
|
|
DIR_GAME_HOME = getPojavStorageRoot(ctx).getAbsolutePath();
|
|
DIR_GAME_NEW = DIR_GAME_HOME + "/.minecraft";
|
|
DIR_HOME_VERSION = DIR_GAME_NEW + "/versions";
|
|
DIR_HOME_LIBRARY = DIR_GAME_NEW + "/libraries";
|
|
DIR_HOME_CRASH = DIR_GAME_NEW + "/crash-reports";
|
|
ASSETS_PATH = DIR_GAME_NEW + "/assets";
|
|
OBSOLETE_RESOURCES_PATH= DIR_GAME_NEW + "/resources";
|
|
CTRLMAP_PATH = DIR_GAME_HOME + "/controlmap";
|
|
CTRLDEF_FILE = DIR_GAME_HOME + "/controlmap/default.json";
|
|
NATIVE_LIB_DIR = ctx.getApplicationInfo().nativeLibraryDir;
|
|
}
|
|
|
|
|
|
public static void launchGLJRE(final Activity activity) throws Throwable {
|
|
|
|
Runtime runtime = MultiRTUtils.forceReread("Internal");
|
|
File gamedir = new File(Tools.DIR_DATA);
|
|
|
|
ExtraCore.setValue(ExtraConstants.OPEN_GL_VERSION, "2");
|
|
|
|
List<String> javaArgList = new ArrayList<>();
|
|
javaArgList.add("-Dorg.lwjgl.util.NoChecks=true");
|
|
|
|
getCacioJavaArgs(javaArgList, runtime.javaVersion == 8);
|
|
|
|
javaArgList.add("-DpluginDir="+ Tools.DIR_DATA + "/plugins/");
|
|
javaArgList.add("-DconfigFile="+Tools.DIR_DATA + "/config.json");
|
|
javaArgList.add("-cp");
|
|
javaArgList.add(getLWJGL3ClassPath()+":"+Tools.DIR_DATA+"/rt4.jar");
|
|
javaArgList.add(RT4_MAIN_CLASS);
|
|
String args = LauncherPreferences.PREF_CUSTOM_JAVA_ARGS;
|
|
JREUtils.launchJavaVM(activity, runtime, gamedir, javaArgList, args);
|
|
}
|
|
|
|
public static File getGameDirPath(@NonNull MinecraftProfile minecraftProfile){
|
|
if(minecraftProfile.gameDir != null){
|
|
if(minecraftProfile.gameDir.startsWith(Tools.LAUNCHERPROFILES_RTPREFIX))
|
|
return new File(minecraftProfile.gameDir.replace(Tools.LAUNCHERPROFILES_RTPREFIX,Tools.DIR_GAME_HOME+"/"));
|
|
else
|
|
return new File(Tools.DIR_GAME_HOME,minecraftProfile.gameDir);
|
|
}
|
|
return new File(Tools.DIR_GAME_NEW);
|
|
}
|
|
|
|
public static void buildNotificationChannel(Context context){
|
|
if(Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return;
|
|
NotificationChannel channel = new NotificationChannel(
|
|
context.getString(R.string.notif_channel_id),
|
|
context.getString(R.string.notif_channel_name), NotificationManager.IMPORTANCE_DEFAULT);
|
|
NotificationManagerCompat manager = NotificationManagerCompat.from(context);
|
|
manager.createNotificationChannel(channel);
|
|
}
|
|
public static void disableSplash(File dir) {
|
|
File configDir = new File(dir, "config");
|
|
if(configDir.exists() || configDir.mkdirs()) {
|
|
File forgeSplashFile = new File(dir, "config/splash.properties");
|
|
String forgeSplashContent = "enabled=true";
|
|
try {
|
|
if (forgeSplashFile.exists()) {
|
|
forgeSplashContent = Tools.read(forgeSplashFile.getAbsolutePath());
|
|
}
|
|
if (forgeSplashContent.contains("enabled=true")) {
|
|
Tools.write(forgeSplashFile.getAbsolutePath(),
|
|
forgeSplashContent.replace("enabled=true", "enabled=false"));
|
|
}
|
|
} catch (IOException e) {
|
|
Log.w(Tools.APP_NAME, "Could not disable Forge 1.12.2 and below splash screen!", e);
|
|
}
|
|
} else {
|
|
Log.w(Tools.APP_NAME, "Failed to create the configuration directory");
|
|
}
|
|
}
|
|
|
|
public static void getCacioJavaArgs(List<String> javaArgList, boolean isJava8) {
|
|
// Caciocavallo config AWT-enabled version
|
|
javaArgList.add("-Djava.awt.headless=false");
|
|
javaArgList.add("-Dcacio.managed.screensize=" + AWTCanvasView.AWT_CANVAS_WIDTH + "x" + AWTCanvasView.AWT_CANVAS_HEIGHT);
|
|
javaArgList.add("-Dcacio.font.fontmanager=sun.awt.X11FontManager");
|
|
javaArgList.add("-Dcacio.font.fontscaler=sun.font.FreetypeFontScaler");
|
|
javaArgList.add("-Dswing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel");
|
|
if (isJava8) {
|
|
javaArgList.add("-Dawt.toolkit=net.java.openjdk.cacio.ctc.CTCToolkit");
|
|
javaArgList.add("-Djava.awt.graphicsenv=net.java.openjdk.cacio.ctc.CTCGraphicsEnvironment");
|
|
} else {
|
|
javaArgList.add("-Dawt.toolkit=com.github.caciocavallosilano.cacio.ctc.CTCToolkit");
|
|
javaArgList.add("-Djava.awt.graphicsenv=com.github.caciocavallosilano.cacio.ctc.CTCGraphicsEnvironment");
|
|
javaArgList.add("-Djava.system.class.loader=com.github.caciocavallosilano.cacio.ctc.CTCPreloadClassLoader");
|
|
|
|
javaArgList.add("--add-exports=java.desktop/java.awt=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/java.awt.peer=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/sun.awt.image=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/sun.java2d=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/java.awt.dnd.peer=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/sun.awt=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/sun.awt.event=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/sun.awt.datatransfer=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.desktop/sun.font=ALL-UNNAMED");
|
|
javaArgList.add("--add-exports=java.base/sun.security.action=ALL-UNNAMED");
|
|
javaArgList.add("--add-opens=java.base/java.util=ALL-UNNAMED");
|
|
javaArgList.add("--add-opens=java.desktop/java.awt=ALL-UNNAMED");
|
|
javaArgList.add("--add-opens=java.desktop/sun.font=ALL-UNNAMED");
|
|
javaArgList.add("--add-opens=java.desktop/sun.java2d=ALL-UNNAMED");
|
|
javaArgList.add("--add-opens=java.base/java.lang.reflect=ALL-UNNAMED");
|
|
|
|
// Opens the java.net package to Arc DNS injector on Java 9+
|
|
javaArgList.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
|
}
|
|
|
|
StringBuilder cacioClasspath = new StringBuilder();
|
|
cacioClasspath.append("-Xbootclasspath/").append(isJava8 ? "p" : "a");
|
|
File cacioDir = new File(DIR_GAME_HOME + "/caciocavallo" + (isJava8 ? "" : "17"));
|
|
File[] cacioFiles = cacioDir.listFiles();
|
|
if (cacioFiles != null) {
|
|
for (File file : cacioFiles) {
|
|
if (file.getName().endsWith(".jar")) {
|
|
cacioClasspath.append(":").append(file.getAbsolutePath());
|
|
}
|
|
}
|
|
}
|
|
javaArgList.add(cacioClasspath.toString());
|
|
}
|
|
|
|
public static String[] getMinecraftJVMArgs(String versionName, File gameDir) {
|
|
JMinecraftVersionList.Version versionInfo = Tools.getVersionInfo(versionName, true);
|
|
// Parse Forge 1.17+ additional JVM Arguments
|
|
if (versionInfo.inheritsFrom == null || versionInfo.arguments == null || versionInfo.arguments.jvm == null) {
|
|
return new String[0];
|
|
}
|
|
|
|
Map<String, String> varArgMap = new ArrayMap<>();
|
|
varArgMap.put("classpath_separator", ":");
|
|
varArgMap.put("library_directory", DIR_HOME_LIBRARY);
|
|
varArgMap.put("version_name", versionInfo.id);
|
|
varArgMap.put("natives_directory", Tools.NATIVE_LIB_DIR);
|
|
|
|
List<String> minecraftArgs = new ArrayList<>();
|
|
if (versionInfo.arguments != null) {
|
|
for (Object arg : versionInfo.arguments.jvm) {
|
|
if (arg instanceof String) {
|
|
minecraftArgs.add((String) arg);
|
|
} //TODO: implement (?maybe?)
|
|
}
|
|
}
|
|
return JSONUtils.insertJSONValueList(minecraftArgs.toArray(new String[0]), varArgMap);
|
|
}
|
|
|
|
public static String[] getMinecraftClientArgs(MinecraftAccount profile, JMinecraftVersionList.Version versionInfo, File gameDir) {
|
|
String username = profile.username;
|
|
String versionName = versionInfo.id;
|
|
if (versionInfo.inheritsFrom != null) {
|
|
versionName = versionInfo.inheritsFrom;
|
|
}
|
|
|
|
String userType = "mojang";
|
|
|
|
Map<String, String> varArgMap = new ArrayMap<>();
|
|
varArgMap.put("auth_session", profile.accessToken); // For legacy versions of MC
|
|
varArgMap.put("auth_access_token", profile.accessToken);
|
|
varArgMap.put("auth_player_name", username);
|
|
varArgMap.put("auth_uuid", profile.profileId.replace("-", ""));
|
|
varArgMap.put("auth_xuid", profile.xuid);
|
|
varArgMap.put("assets_root", Tools.ASSETS_PATH);
|
|
varArgMap.put("assets_index_name", versionInfo.assets);
|
|
varArgMap.put("game_assets", Tools.ASSETS_PATH);
|
|
varArgMap.put("game_directory", gameDir.getAbsolutePath());
|
|
varArgMap.put("user_properties", "{}");
|
|
varArgMap.put("user_type", userType);
|
|
varArgMap.put("version_name", versionName);
|
|
varArgMap.put("version_type", versionInfo.type);
|
|
|
|
List<String> minecraftArgs = new ArrayList<>();
|
|
if (versionInfo.arguments != null) {
|
|
// Support Minecraft 1.13+
|
|
for (Object arg : versionInfo.arguments.game) {
|
|
if (arg instanceof String) {
|
|
minecraftArgs.add((String) arg);
|
|
} //TODO: implement else clause
|
|
}
|
|
}
|
|
|
|
return JSONUtils.insertJSONValueList(
|
|
splitAndFilterEmpty(
|
|
versionInfo.minecraftArguments == null ?
|
|
fromStringArray(minecraftArgs.toArray(new String[0])):
|
|
versionInfo.minecraftArguments
|
|
), varArgMap
|
|
);
|
|
}
|
|
|
|
public static String fromStringArray(String[] strArr) {
|
|
StringBuilder builder = new StringBuilder();
|
|
for (int i = 0; i < strArr.length; i++) {
|
|
if (i > 0) builder.append(" ");
|
|
builder.append(strArr[i]);
|
|
}
|
|
|
|
return builder.toString();
|
|
}
|
|
|
|
private static String[] splitAndFilterEmpty(String argStr) {
|
|
List<String> strList = new ArrayList<>();
|
|
for (String arg : argStr.split(" ")) {
|
|
if (!arg.isEmpty()) {
|
|
strList.add(arg);
|
|
}
|
|
}
|
|
//strList.add("--fullscreen");
|
|
return strList.toArray(new String[0]);
|
|
}
|
|
|
|
public static String artifactToPath(DependentLibrary library) {
|
|
if (library.downloads != null &&
|
|
library.downloads.artifact != null &&
|
|
library.downloads.artifact.path != null)
|
|
return library.downloads.artifact.path;
|
|
String[] libInfos = library.name.split(":");
|
|
return libInfos[0].replaceAll("\\.", "/") + "/" + libInfos[1] + "/" + libInfos[2] + "/" + libInfos[1] + "-" + libInfos[2] + ".jar";
|
|
}
|
|
|
|
public static String getPatchedFile(String version) {
|
|
return DIR_HOME_VERSION + "/" + version + "/" + version + ".jar";
|
|
}
|
|
|
|
private static String getLWJGL3ClassPath() {
|
|
StringBuilder libStr = new StringBuilder();
|
|
File lwjgl3Folder = new File(Tools.DIR_GAME_HOME, "lwjgl3");
|
|
File[] lwjgl3Files = lwjgl3Folder.listFiles();
|
|
if (lwjgl3Files != null) {
|
|
for (File file: lwjgl3Files) {
|
|
if (file.getName().endsWith(".jar")) {
|
|
libStr.append(file.getAbsolutePath()).append(":");
|
|
}
|
|
}
|
|
}
|
|
// Remove the ':' at the end
|
|
libStr.setLength(libStr.length() - 1);
|
|
return libStr.toString();
|
|
}
|
|
|
|
private final static boolean isClientFirst = false;
|
|
public static String generateLaunchClassPath(JMinecraftVersionList.Version info,String actualname) {
|
|
StringBuilder libStr = new StringBuilder(); //versnDir + "/" + version + "/" + version + ".jar:";
|
|
|
|
String[] classpath = generateLibClasspath(info);
|
|
|
|
if (isClientFirst) {
|
|
libStr.append(getPatchedFile(actualname));
|
|
}
|
|
for (String perJar : classpath) {
|
|
if (!new File(perJar).exists()) {
|
|
Log.d(APP_NAME, "Ignored non-exists file: " + perJar);
|
|
continue;
|
|
}
|
|
libStr.append((isClientFirst ? ":" : "")).append(perJar).append(!isClientFirst ? ":" : "");
|
|
}
|
|
if (!isClientFirst) {
|
|
libStr.append(getPatchedFile(actualname));
|
|
}
|
|
|
|
return libStr.toString();
|
|
}
|
|
|
|
public static DisplayMetrics getDisplayMetrics(Activity activity) {
|
|
DisplayMetrics displayMetrics = new DisplayMetrics();
|
|
|
|
if(SDK_INT >= Build.VERSION_CODES.N && (activity.isInMultiWindowMode() || activity.isInPictureInPictureMode())){
|
|
//For devices with free form/split screen, we need window size, not screen size.
|
|
displayMetrics = activity.getResources().getDisplayMetrics();
|
|
}else{
|
|
if (SDK_INT >= Build.VERSION_CODES.R) {
|
|
activity.getDisplay().getRealMetrics(displayMetrics);
|
|
} else { // Removed the clause for devices with unofficial notch support, since it also ruins all devices with virtual nav bars before P
|
|
activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
|
|
}
|
|
if(!PREF_IGNORE_NOTCH){
|
|
//Remove notch width when it isn't ignored.
|
|
if(activity.getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT)
|
|
displayMetrics.heightPixels -= PREF_NOTCH_SIZE;
|
|
else
|
|
displayMetrics.widthPixels -= PREF_NOTCH_SIZE;
|
|
}
|
|
}
|
|
currentDisplayMetrics = displayMetrics;
|
|
return displayMetrics;
|
|
}
|
|
|
|
public static void setFullscreen(Activity activity, boolean fullscreen) {
|
|
final View decorView = activity.getWindow().getDecorView();
|
|
View.OnSystemUiVisibilityChangeListener visibilityChangeListener = visibility -> {
|
|
if(fullscreen){
|
|
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
|
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
|
| View.SYSTEM_UI_FLAG_FULLSCREEN
|
|
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
|
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
|
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
|
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
|
}
|
|
}else{
|
|
decorView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
|
|
}
|
|
|
|
};
|
|
decorView.setOnSystemUiVisibilityChangeListener(visibilityChangeListener);
|
|
visibilityChangeListener.onSystemUiVisibilityChange(decorView.getSystemUiVisibility()); //call it once since the UI state may not change after the call, so the activity wont become fullscreen
|
|
}
|
|
|
|
public static DisplayMetrics currentDisplayMetrics;
|
|
|
|
public static void updateWindowSize(Activity activity) {
|
|
currentDisplayMetrics = getDisplayMetrics(activity);
|
|
|
|
CallbackBridge.physicalWidth = currentDisplayMetrics.widthPixels;
|
|
CallbackBridge.physicalHeight = currentDisplayMetrics.heightPixels;
|
|
}
|
|
|
|
public static float dpToPx(float dp) {
|
|
//Better hope for the currentDisplayMetrics to be good
|
|
return dp * currentDisplayMetrics.density;
|
|
}
|
|
|
|
public static float pxToDp(float px){
|
|
//Better hope for the currentDisplayMetrics to be good
|
|
return px / currentDisplayMetrics.density;
|
|
}
|
|
|
|
public static void copyAssetFile(Context ctx, String fileName, String output, boolean overwrite) throws IOException {
|
|
copyAssetFile(ctx, fileName, output, new File(fileName).getName(), overwrite);
|
|
}
|
|
|
|
public static void copyAssetFile(Context ctx, String fileName, String output, String outputName, boolean overwrite) throws IOException {
|
|
File parentFolder = new File(output);
|
|
if(!parentFolder.exists() && !parentFolder.mkdirs()) {
|
|
throw new IOException("Failed to create parent directory");
|
|
}
|
|
File destinationFile = new File(output, outputName);
|
|
if(!destinationFile.exists() || overwrite){
|
|
try(InputStream inputStream = ctx.getAssets().open(fileName)) {
|
|
try (OutputStream outputStream = new FileOutputStream(destinationFile)){
|
|
IOUtils.copy(inputStream, outputStream);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public static String printToString(Throwable throwable) {
|
|
StringWriter stringWriter = new StringWriter();
|
|
PrintWriter printWriter = new PrintWriter(stringWriter);
|
|
throwable.printStackTrace(printWriter);
|
|
printWriter.close();
|
|
return stringWriter.toString();
|
|
}
|
|
|
|
public static void showError(Context ctx, Throwable e) {
|
|
showError(ctx, e, false);
|
|
}
|
|
|
|
public static void showError(final Context ctx, final Throwable e, final boolean exitIfOk) {
|
|
showError(ctx, R.string.global_error, null ,e, exitIfOk, false);
|
|
}
|
|
public static void showError(final Context ctx, final int rolledMessage, final Throwable e) {
|
|
showError(ctx, R.string.global_error, ctx.getString(rolledMessage), e, false, false);
|
|
}
|
|
public static void showError(final Context ctx, final String rolledMessage, final Throwable e) {
|
|
showError(ctx, R.string.global_error, rolledMessage, e, false, false);
|
|
}
|
|
|
|
public static void showError(final Context ctx, final int titleId, final Throwable e, final boolean exitIfOk) {
|
|
showError(ctx, titleId, null, e, exitIfOk, false);
|
|
}
|
|
|
|
private static void showError(final Context ctx, final int titleId, final String rolledMessage, final Throwable e, final boolean exitIfOk, final boolean showMore) {
|
|
e.printStackTrace();
|
|
|
|
Runnable runnable = () -> {
|
|
final String errMsg = showMore ? printToString(e) : rolledMessage != null ? rolledMessage : e.getMessage();
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(ctx)
|
|
.setTitle(titleId)
|
|
.setMessage(errMsg)
|
|
.setPositiveButton(android.R.string.ok, (p1, p2) -> {
|
|
if(exitIfOk) {
|
|
if (ctx instanceof MainActivity) {
|
|
MainActivity.fullyExit();
|
|
} else if (ctx instanceof Activity) {
|
|
((Activity) ctx).finish();
|
|
}
|
|
}
|
|
})
|
|
.setNegativeButton(showMore ? R.string.error_show_less : R.string.error_show_more, (p1, p2) -> showError(ctx, titleId, rolledMessage, e, exitIfOk, !showMore))
|
|
.setNeutralButton(android.R.string.copy, (p1, p2) -> {
|
|
ClipboardManager mgr = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
|
|
mgr.setPrimaryClip(ClipData.newPlainText("error", Log.getStackTraceString(e)));
|
|
if(exitIfOk) {
|
|
if (ctx instanceof MainActivity) {
|
|
MainActivity.fullyExit();
|
|
} else {
|
|
((Activity) ctx).finish();
|
|
}
|
|
}
|
|
})
|
|
.setCancelable(!exitIfOk);
|
|
try {
|
|
builder.show();
|
|
} catch (Throwable th) {
|
|
th.printStackTrace();
|
|
}
|
|
};
|
|
|
|
if (ctx instanceof Activity) {
|
|
((Activity) ctx).runOnUiThread(runnable);
|
|
} else {
|
|
runnable.run();
|
|
}
|
|
}
|
|
|
|
public static void dialogOnUiThread(final Activity activity, final CharSequence title, final CharSequence message) {
|
|
activity.runOnUiThread(()->dialog(activity, title, message));
|
|
}
|
|
|
|
public static void dialog(final Context context, final CharSequence title, final CharSequence message) {
|
|
new AlertDialog.Builder(context)
|
|
.setTitle(title)
|
|
.setMessage(message)
|
|
.setPositiveButton(android.R.string.ok, null)
|
|
.show();
|
|
}
|
|
|
|
public static void openURL(Activity act, String url) {
|
|
Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
|
act.startActivity(browserIntent);
|
|
}
|
|
|
|
private static boolean checkRules(JMinecraftVersionList.Arguments.ArgValue.ArgRules[] rules) {
|
|
if(rules == null) return true; // always allow
|
|
for (JMinecraftVersionList.Arguments.ArgValue.ArgRules rule : rules) {
|
|
if (rule.action.equals("allow") && rule.os != null && rule.os.name.equals("osx")) {
|
|
return false; //disallow
|
|
}
|
|
}
|
|
return true; // allow if none match
|
|
}
|
|
public static String[] generateLibClasspath(JMinecraftVersionList.Version info) {
|
|
List<String> libDir = new ArrayList<>();
|
|
for (DependentLibrary libItem: info.libraries) {
|
|
if(!checkRules(libItem.rules)) continue;
|
|
libDir.add(Tools.DIR_HOME_LIBRARY + "/" + artifactToPath(libItem));
|
|
}
|
|
return libDir.toArray(new String[0]);
|
|
}
|
|
|
|
public static JMinecraftVersionList.Version getVersionInfo(String versionName) {
|
|
return getVersionInfo(versionName, false);
|
|
}
|
|
|
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
|
public static JMinecraftVersionList.Version getVersionInfo(String versionName, boolean skipInheriting) {
|
|
try {
|
|
JMinecraftVersionList.Version customVer = Tools.GLOBAL_GSON.fromJson(read(DIR_HOME_VERSION + "/" + versionName + "/" + versionName + ".json"), JMinecraftVersionList.Version.class);
|
|
if (skipInheriting || customVer.inheritsFrom == null || customVer.inheritsFrom.equals(customVer.id)) {
|
|
return customVer;
|
|
} else {
|
|
JMinecraftVersionList.Version inheritsVer;
|
|
//If it won't download, just search for it
|
|
try{
|
|
inheritsVer = Tools.GLOBAL_GSON.fromJson(read(DIR_HOME_VERSION + "/" + customVer.inheritsFrom + "/" + customVer.inheritsFrom + ".json"), JMinecraftVersionList.Version.class);
|
|
}catch(IOException e) {
|
|
throw new RuntimeException("Can't find the source version for "+ versionName +" (req version="+customVer.inheritsFrom+")");
|
|
}
|
|
//inheritsVer.inheritsFrom = inheritsVer.id;
|
|
insertSafety(inheritsVer, customVer,
|
|
"assetIndex", "assets", "id",
|
|
"mainClass", "minecraftArguments",
|
|
"releaseTime", "time", "type"
|
|
);
|
|
|
|
List<DependentLibrary> libList = new ArrayList<>(Arrays.asList(inheritsVer.libraries));
|
|
try {
|
|
loop_1:
|
|
for (DependentLibrary lib : customVer.libraries) {
|
|
String libName = lib.name.substring(0, lib.name.lastIndexOf(":"));
|
|
for (int i = 0; i < libList.size(); i++) {
|
|
DependentLibrary libAdded = libList.get(i);
|
|
String libAddedName = libAdded.name.substring(0, libAdded.name.lastIndexOf(":"));
|
|
|
|
if (libAddedName.equals(libName)) {
|
|
Log.d(APP_NAME, "Library " + libName + ": Replaced version " +
|
|
libName.substring(libName.lastIndexOf(":") + 1) + " with " +
|
|
libAddedName.substring(libAddedName.lastIndexOf(":") + 1));
|
|
libList.set(i, lib);
|
|
continue loop_1;
|
|
}
|
|
}
|
|
|
|
libList.add(0, lib);
|
|
}
|
|
} finally {
|
|
inheritsVer.libraries = libList.toArray(new DependentLibrary[0]);
|
|
}
|
|
|
|
// Inheriting Minecraft 1.13+ with append custom args
|
|
if (inheritsVer.arguments != null && customVer.arguments != null) {
|
|
List totalArgList = new ArrayList(Arrays.asList(inheritsVer.arguments.game));
|
|
|
|
int nskip = 0;
|
|
for (int i = 0; i < customVer.arguments.game.length; i++) {
|
|
if (nskip > 0) {
|
|
nskip--;
|
|
continue;
|
|
}
|
|
|
|
Object perCustomArg = customVer.arguments.game[i];
|
|
if (perCustomArg instanceof String) {
|
|
String perCustomArgStr = (String) perCustomArg;
|
|
// Check if there is a duplicate argument on combine
|
|
if (perCustomArgStr.startsWith("--") && totalArgList.contains(perCustomArgStr)) {
|
|
perCustomArg = customVer.arguments.game[i + 1];
|
|
if (perCustomArg instanceof String) {
|
|
perCustomArgStr = (String) perCustomArg;
|
|
// If the next is argument value, skip it
|
|
if (!perCustomArgStr.startsWith("--")) {
|
|
nskip++;
|
|
}
|
|
}
|
|
} else {
|
|
totalArgList.add(perCustomArgStr);
|
|
}
|
|
} else if (!totalArgList.contains(perCustomArg)) {
|
|
totalArgList.add(perCustomArg);
|
|
}
|
|
}
|
|
|
|
inheritsVer.arguments.game = totalArgList.toArray(new Object[0]);
|
|
}
|
|
|
|
return inheritsVer;
|
|
}
|
|
} catch (Exception e) {
|
|
throw new RuntimeException(e);
|
|
}
|
|
}
|
|
|
|
// Prevent NullPointerException
|
|
private static void insertSafety(JMinecraftVersionList.Version targetVer, JMinecraftVersionList.Version fromVer, String... keyArr) {
|
|
for (String key : keyArr) {
|
|
Object value = null;
|
|
try {
|
|
Field fieldA = fromVer.getClass().getDeclaredField(key);
|
|
value = fieldA.get(fromVer);
|
|
if (((value instanceof String) && !((String) value).isEmpty()) || value != null) {
|
|
Field fieldB = targetVer.getClass().getDeclaredField(key);
|
|
fieldB.set(targetVer, value);
|
|
}
|
|
} catch (Throwable th) {
|
|
Log.w(Tools.APP_NAME, "Unable to insert " + key + "=" + value, th);
|
|
}
|
|
}
|
|
}
|
|
|
|
public static String read(InputStream is) throws IOException {
|
|
String readResult = IOUtils.toString(is, StandardCharsets.UTF_8);
|
|
is.close();
|
|
return readResult;
|
|
}
|
|
|
|
public static String read(String path) throws IOException {
|
|
return read(new FileInputStream(path));
|
|
}
|
|
|
|
public static void write(String path, String content) throws IOException {
|
|
File file = new File(path);
|
|
File parent = file.getParentFile();
|
|
if(parent != null && !parent.exists()) {
|
|
if(!parent.mkdirs()) throw new IOException("Failed to create parent directory");
|
|
}
|
|
try(FileOutputStream outStream = new FileOutputStream(file)) {
|
|
IOUtils.write(content, outStream);
|
|
}
|
|
}
|
|
|
|
public static void downloadFile(String urlInput, String nameOutput) throws IOException {
|
|
File file = new File(nameOutput);
|
|
DownloadUtils.downloadFile(urlInput, file);
|
|
}
|
|
|
|
public interface DownloaderFeedback {
|
|
void updateProgress(int curr, int max);
|
|
}
|
|
|
|
public static class ZipTool
|
|
{
|
|
private ZipTool(){}
|
|
public static void zip(List<File> files, File zipFile) throws IOException {
|
|
final int BUFFER_SIZE = 2048;
|
|
|
|
BufferedInputStream origin = null;
|
|
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
|
|
|
|
try {
|
|
byte data[] = new byte[BUFFER_SIZE];
|
|
|
|
for (File file : files) {
|
|
FileInputStream fileInputStream = new FileInputStream( file );
|
|
|
|
origin = new BufferedInputStream(fileInputStream, BUFFER_SIZE);
|
|
|
|
try {
|
|
ZipEntry entry = new ZipEntry(file.getName());
|
|
|
|
out.putNextEntry(entry);
|
|
|
|
int count;
|
|
while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
|
|
out.write(data, 0, count);
|
|
}
|
|
}
|
|
finally {
|
|
origin.close();
|
|
}
|
|
}
|
|
} finally {
|
|
out.close();
|
|
}
|
|
}
|
|
public static void unzip(File zipFile, File targetDirectory) throws IOException {
|
|
final int BUFFER_SIZE = 1024;
|
|
ZipInputStream zis = new ZipInputStream(
|
|
new BufferedInputStream(new FileInputStream(zipFile)));
|
|
try {
|
|
ZipEntry ze;
|
|
int count;
|
|
byte[] buffer = new byte[BUFFER_SIZE];
|
|
while ((ze = zis.getNextEntry()) != null) {
|
|
File file = new File(targetDirectory, ze.getName());
|
|
File dir = ze.isDirectory() ? file : file.getParentFile();
|
|
if (!dir.isDirectory() && !dir.mkdirs())
|
|
throw new FileNotFoundException("Failed to ensure directory: " +
|
|
dir.getAbsolutePath());
|
|
if (ze.isDirectory())
|
|
continue;
|
|
FileOutputStream fout = new FileOutputStream(file);
|
|
try {
|
|
while ((count = zis.read(buffer)) != -1)
|
|
fout.write(buffer, 0, count);
|
|
} finally {
|
|
fout.close();
|
|
}
|
|
}
|
|
} finally {
|
|
zis.close();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
public static boolean compareSHA1(File f, String sourceSHA) {
|
|
try {
|
|
String sha1_dst;
|
|
try (InputStream is = new FileInputStream(f)) {
|
|
sha1_dst = new String(Hex.encodeHex(org.apache.commons.codec.digest.DigestUtils.sha1(is)));
|
|
}
|
|
if(sourceSHA != null) {
|
|
return sha1_dst.equalsIgnoreCase(sourceSHA);
|
|
} else{
|
|
return true; // fake match
|
|
}
|
|
}catch (IOException e) {
|
|
Log.i("SHA1","Fake-matching a hash due to a read error",e);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public static void ignoreNotch(boolean shouldIgnore, Activity ctx){
|
|
if (SDK_INT >= P) {
|
|
if (shouldIgnore) {
|
|
ctx.getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES;
|
|
} else {
|
|
ctx.getWindow().getAttributes().layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_NEVER;
|
|
}
|
|
ctx.getWindow().setFlags(WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN, WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN);
|
|
Tools.updateWindowSize(ctx);
|
|
}
|
|
}
|
|
|
|
public static int getTotalDeviceMemory(Context ctx){
|
|
ActivityManager actManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
|
|
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
|
|
actManager.getMemoryInfo(memInfo);
|
|
return (int) (memInfo.totalMem / 1048576L);
|
|
}
|
|
|
|
public static int getFreeDeviceMemory(Context ctx){
|
|
ActivityManager actManager = (ActivityManager) ctx.getSystemService(Context.ACTIVITY_SERVICE);
|
|
ActivityManager.MemoryInfo memInfo = new ActivityManager.MemoryInfo();
|
|
actManager.getMemoryInfo(memInfo);
|
|
return (int) (memInfo.availMem / 1048576L);
|
|
}
|
|
|
|
public static int getDisplayFriendlyRes(int displaySideRes, float scaling){
|
|
displaySideRes *= scaling;
|
|
if(displaySideRes % 2 != 0) displaySideRes ++;
|
|
return displaySideRes;
|
|
}
|
|
|
|
public static String getFileName(Context ctx, Uri uri) {
|
|
Cursor c = ctx.getContentResolver().query(uri, null, null, null, null);
|
|
if(c == null) return uri.getLastPathSegment(); // idk myself but it happens on asus file manager
|
|
c.moveToFirst();
|
|
int columnIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
|
if(columnIndex == -1) return uri.getLastPathSegment();
|
|
String fileName = c.getString(columnIndex);
|
|
c.close();
|
|
return fileName;
|
|
}
|
|
|
|
/** Swap the main fragment with another */
|
|
public static void swapFragment(FragmentActivity fragmentActivity , Class<? extends Fragment> fragmentClass,
|
|
@Nullable String fragmentTag, boolean addCurrentToBackstack, @Nullable Bundle bundle) {
|
|
// When people tab out, it might happen
|
|
//TODO handle custom animations
|
|
FragmentTransaction transaction = fragmentActivity.getSupportFragmentManager().beginTransaction()
|
|
.setReorderingAllowed(true)
|
|
.replace(R.id.container_fragment, fragmentClass, bundle, fragmentTag);
|
|
if(addCurrentToBackstack) transaction.addToBackStack(null);
|
|
|
|
transaction.commit();
|
|
}
|
|
|
|
/** Remove the current fragment */
|
|
public static void removeCurrentFragment(FragmentActivity fragmentActivity){
|
|
fragmentActivity.getSupportFragmentManager().popBackStackImmediate();
|
|
}
|
|
|
|
public static void installMod(Activity activity, boolean customJavaArgs) {
|
|
if (MultiRTUtils.getExactJreName(8) == null) {
|
|
Toast.makeText(activity, R.string.multirt_nojava8rt, Toast.LENGTH_LONG).show();
|
|
return;
|
|
}
|
|
|
|
if(!customJavaArgs){ // Launch the intent to get the jar file
|
|
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
|
|
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
|
String mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension("jar");
|
|
if(mimeType == null) mimeType = "*/*";
|
|
intent.setType(mimeType);
|
|
activity.startActivityForResult(intent, RUN_MOD_INSTALLER);
|
|
return;
|
|
}
|
|
|
|
// install mods with custom arguments
|
|
final EditText editText = new EditText(activity);
|
|
editText.setSingleLine();
|
|
editText.setHint("-jar/-cp /path/to/file.jar ...");
|
|
|
|
AlertDialog.Builder builder = new AlertDialog.Builder(activity)
|
|
.setTitle(R.string.alerttitle_installmod)
|
|
.setNegativeButton(android.R.string.cancel, null)
|
|
.setView(editText)
|
|
.setPositiveButton(android.R.string.ok, (di, i) -> {
|
|
Intent intent = new Intent(activity, JavaGUILauncherActivity.class);
|
|
intent.putExtra("skipDetectMod", true);
|
|
intent.putExtra("javaArgs", editText.getText().toString());
|
|
activity.startActivity(intent);
|
|
});
|
|
builder.show();
|
|
}
|
|
|
|
/** Display and return a progress dialog, instructing to wait */
|
|
private static ProgressDialog getWaitingDialog(Context ctx){
|
|
final ProgressDialog barrier = new ProgressDialog(ctx);
|
|
barrier.setMessage(ctx.getString(R.string.global_waiting));
|
|
barrier.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
|
barrier.setCancelable(false);
|
|
barrier.show();
|
|
|
|
return barrier;
|
|
}
|
|
|
|
/** Copy the mod file, and launch the mod installer activity */
|
|
public static void launchModInstaller(Activity activity, @NonNull Intent data){
|
|
final ProgressDialog alertDialog = getWaitingDialog(activity);
|
|
|
|
final Uri uri = data.getData();
|
|
alertDialog.setMessage(activity.getString(R.string.multirt_progress_caching));
|
|
sExecutorService.execute(() -> {
|
|
try {
|
|
final String name = getFileName(activity, uri);
|
|
final File modInstallerFile = new File(Tools.DIR_CACHE, name);
|
|
FileOutputStream fos = new FileOutputStream(modInstallerFile);
|
|
InputStream input = activity.getContentResolver().openInputStream(uri);
|
|
IOUtils.copy(input, fos);
|
|
input.close();
|
|
fos.close();
|
|
activity.runOnUiThread(() -> {
|
|
alertDialog.dismiss();
|
|
Intent intent = new Intent(activity, JavaGUILauncherActivity.class);
|
|
intent.putExtra("modFile", modInstallerFile);
|
|
activity.startActivity(intent);
|
|
});
|
|
}catch(IOException e) {
|
|
Tools.showError(activity, e);
|
|
}
|
|
});
|
|
}
|
|
|
|
|
|
public static void installRuntimeFromUri(Activity activity, Uri uri){
|
|
sExecutorService.execute(() -> {
|
|
try {
|
|
String name = getFileName(activity, uri);
|
|
MultiRTUtils.installRuntimeNamed(
|
|
NATIVE_LIB_DIR,
|
|
activity.getContentResolver().openInputStream(uri),
|
|
name);
|
|
|
|
MultiRTUtils.postPrepare(name);
|
|
} catch (IOException e) {
|
|
Tools.showError(activity, e);
|
|
}
|
|
});
|
|
}
|
|
|
|
public static String extractUntilCharacter(String input, String whatFor, char terminator) {
|
|
int whatForStart = input.indexOf(whatFor);
|
|
if(whatForStart == -1) return null;
|
|
whatForStart += whatFor.length();
|
|
int terminatorIndex = input.indexOf(terminator, whatForStart);
|
|
if(terminatorIndex == -1) return null;
|
|
return input.substring(whatForStart, terminatorIndex);
|
|
}
|
|
|
|
public static boolean isValidString(String string) {
|
|
return string != null && !string.isEmpty();
|
|
}
|
|
|
|
public static String getRuntimeName(String prefixedName) {
|
|
if(prefixedName == null) return prefixedName;
|
|
if(!prefixedName.startsWith(Tools.LAUNCHERPROFILES_RTPREFIX)) return null;
|
|
return prefixedName.substring(Tools.LAUNCHERPROFILES_RTPREFIX.length());
|
|
}
|
|
|
|
public static String getSelectedRuntime(MinecraftProfile minecraftProfile) {
|
|
String runtime = LauncherPreferences.PREF_DEFAULT_RUNTIME;
|
|
String profileRuntime = getRuntimeName(minecraftProfile.javaDir);
|
|
if(profileRuntime != null) {
|
|
if(MultiRTUtils.forceReread(profileRuntime).versionString != null) {
|
|
runtime = profileRuntime;
|
|
}
|
|
}
|
|
return runtime;
|
|
}
|
|
|
|
public static void runOnUiThread(Runnable runnable) {
|
|
MAIN_HANDLER.post(runnable);
|
|
}
|
|
|
|
public static @NonNull String pickRuntime(MinecraftProfile minecraftProfile, int targetJavaVersion) {
|
|
String runtime = getSelectedRuntime(minecraftProfile);
|
|
String profileRuntime = getRuntimeName(minecraftProfile.javaDir);
|
|
Runtime pickedRuntime = MultiRTUtils.read(runtime);
|
|
if(runtime == null || pickedRuntime.javaVersion == 0 || pickedRuntime.javaVersion < targetJavaVersion) {
|
|
String preferredRuntime = MultiRTUtils.getNearestJreName(targetJavaVersion);
|
|
if(preferredRuntime == null) throw new RuntimeException("Failed to autopick runtime!");
|
|
if(profileRuntime != null) minecraftProfile.javaDir = Tools.LAUNCHERPROFILES_RTPREFIX+preferredRuntime;
|
|
runtime = preferredRuntime;
|
|
}
|
|
return runtime;
|
|
}
|
|
|
|
/** Triggers the share intent chooser, with the latestlog file attached to it */
|
|
public static void shareLog(Context context){
|
|
Uri contentUri = DocumentsContract.buildDocumentUri(context.getString(R.string.storageProviderAuthorities), Tools.DIR_GAME_HOME + "/latestlog.txt");
|
|
|
|
Intent shareIntent = new Intent();
|
|
shareIntent.setAction(Intent.ACTION_SEND);
|
|
shareIntent.putExtra(Intent.EXTRA_STREAM, contentUri);
|
|
shareIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
shareIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
shareIntent.setType("text/plain");
|
|
|
|
Intent sendIntent = Intent.createChooser(shareIntent, "latestlog.txt");
|
|
context.startActivity(sendIntent);
|
|
}
|
|
}
|