From 2cc93924f1915ce6e427b8b53566faa01ab55757 Mon Sep 17 00:00:00 2001 From: Anuken Date: Fri, 25 Sep 2020 14:21:30 -0400 Subject: [PATCH] Support for Android java mods --- .../mindustry/android/AndroidLauncher.java | 32 +++++++++++++++---- core/src/mindustry/core/Platform.java | 10 +++++- core/src/mindustry/mod/Mods.java | 8 ++--- core/src/mindustry/net/CrashSender.java | 2 +- 4 files changed, 39 insertions(+), 13 deletions(-) diff --git a/android/src/mindustry/android/AndroidLauncher.java b/android/src/mindustry/android/AndroidLauncher.java index e2c484e50d..7400bd37a8 100644 --- a/android/src/mindustry/android/AndroidLauncher.java +++ b/android/src/mindustry/android/AndroidLauncher.java @@ -7,7 +7,6 @@ import android.content.pm.*; import android.net.*; import android.os.Build.*; import android.os.*; -import android.provider.Settings.*; import android.telephony.*; import arc.*; import arc.backend.android.*; @@ -15,7 +14,7 @@ import arc.files.*; import arc.func.*; import arc.scene.ui.layout.*; import arc.util.*; -import arc.util.serialization.*; +import dalvik.system.*; import mindustry.*; import mindustry.game.Saves.*; import mindustry.io.*; @@ -23,7 +22,6 @@ import mindustry.net.*; import mindustry.ui.dialogs.*; import java.io.*; -import java.lang.System; import java.lang.Thread.*; import java.util.*; @@ -73,12 +71,25 @@ public class AndroidLauncher extends AndroidApplication{ public void shareFile(Fi file){ } + @Override + public Class loadJar(Fi jar, String mainClass) throws Exception{ + DexClassLoader loader = new DexClassLoader(jar.file().getPath(), getFilesDir().getPath(), null, getClassLoader()); + return Class.forName(mainClass, true, loader); + } + @Override public void showFileChooser(boolean open, String extension, Cons cons){ + showFileChooser(open, cons, extension); + } + + void showFileChooser(boolean open, Cons cons, String... extensions){ + String extension = extensions[0]; + if(VERSION.SDK_INT >= VERSION_CODES.Q){ Intent intent = new Intent(open ? Intent.ACTION_OPEN_DOCUMENT : Intent.ACTION_CREATE_DOCUMENT); intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType(extension.equals("zip") && !open ? "application/zip" : "*/*"); + intent.setType(extension.equals("zip") && !open && extensions.length == 1 ? "application/zip" : "*/*"); + addResultListener(i -> startActivityForResult(intent, i), (code, in) -> { if(code == Activity.RESULT_OK && in != null && in.getData() != null){ Uri uri = in.getData(); @@ -108,7 +119,7 @@ public class AndroidLauncher extends AndroidApplication{ }); }else if(VERSION.SDK_INT >= VERSION_CODES.M && !(checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED && checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED)){ - chooser = new FileChooser(open ? "@open" : "@save", file -> file.extension().equalsIgnoreCase(extension), open, file -> { + chooser = new FileChooser(open ? "@open" : "@save", file -> Structs.contains(extensions, file.extension().toLowerCase()), open, file -> { if(!open){ cons.get(file.parent().child(file.nameWithoutExtension() + "." + extension)); }else{ @@ -125,10 +136,19 @@ public class AndroidLauncher extends AndroidApplication{ } requestPermissions(perms.toArray(new String[0]), PERMISSION_REQUEST_CODE); }else{ - super.showFileChooser(open, extension, cons); + if(open){ + new FileChooser("@open", file -> Structs.contains(extensions, file.extension().toLowerCase()), true, cons).show(); + }else{ + super.showFileChooser(open, extension, cons); + } } } + @Override + public void showMultiFileChooser(Cons cons, String... extensions){ + showFileChooser(true, cons, extensions); + } + @Override public void beginForceLandscape(){ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE); diff --git a/core/src/mindustry/core/Platform.java b/core/src/mindustry/core/Platform.java index ff7d44ec88..7eae8136c0 100644 --- a/core/src/mindustry/core/Platform.java +++ b/core/src/mindustry/core/Platform.java @@ -14,10 +14,18 @@ import mindustry.type.*; import mindustry.ui.dialogs.*; import rhino.*; +import java.net.*; + import static mindustry.Vars.*; public interface Platform{ + /** Dynamically loads a jar file. */ + default Class loadJar(Fi jar, String mainClass) throws Exception{ + URLClassLoader classLoader = new URLClassLoader(new URL[]{jar.file().toURI().toURL()}, ClassLoader.getSystemClassLoader()); + return classLoader.loadClass(mainClass); + } + /** Steam: Update lobby visibility.*/ default void updateLobby(){} @@ -121,7 +129,7 @@ public interface Platform{ } /** - * Show a file chooser for multiple file types. Only supported on desktop. + * Show a file chooser for multiple file types. * @param cons Selection listener * @param extensions File extensions to filter */ diff --git a/core/src/mindustry/mod/Mods.java b/core/src/mindustry/mod/Mods.java index 9aeee4ab2f..89f378f406 100644 --- a/core/src/mindustry/mod/Mods.java +++ b/core/src/mindustry/mod/Mods.java @@ -25,7 +25,6 @@ import mindustry.type.*; import mindustry.ui.*; import java.io.*; -import java.net.*; import static mindustry.Vars.*; @@ -634,12 +633,11 @@ public class Mods implements Loadable{ //make sure the main class exists before loading it; if it doesn't just don't put it there if(mainFile.exists()){ //mobile versions don't support class mods - if(mobile){ - throw new IllegalArgumentException("Java class mods are not supported on mobile."); + if(ios){ + throw new IllegalArgumentException("Java class mods are not supported on iOS."); } - URLClassLoader classLoader = new URLClassLoader(new URL[]{sourceFile.file().toURI().toURL()}, ClassLoader.getSystemClassLoader()); - Class main = classLoader.loadClass(mainClass); + Class main = platform.loadJar(sourceFile, mainClass); metas.put(main, meta); mainMod = (Mod)main.getDeclaredConstructor().newInstance(); }else{ diff --git a/core/src/mindustry/net/CrashSender.java b/core/src/mindustry/net/CrashSender.java index 2d31c25ca8..5a259f7721 100644 --- a/core/src/mindustry/net/CrashSender.java +++ b/core/src/mindustry/net/CrashSender.java @@ -33,7 +33,7 @@ public class CrashSender{ + "OS: " + System.getProperty("os.name") + " x" + (OS.is64Bit ? "64" : "32") + "\n" + "Java Version: " + System.getProperty("java.version") + "\n" + "Java Architecture: " + System.getProperty("sun.arch.data.model") + "\n" - + mods.list().size + " Mods: " + mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version) + + mods.list().size + " Mods: " + (mods.list().isEmpty() ? "none" : mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version)) + "\n\n" + error; }