├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── app ├── .gitignore ├── build.gradle ├── libs │ ├── json-20190722.jar │ └── rhino-1.7.12.jar ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── mjtg │ │ └── neutron │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ ├── classes.dex │ │ └── pre.js │ ├── java │ │ └── com │ │ │ └── mjtg │ │ │ └── neutron │ │ │ ├── AboutActivity.java │ │ │ ├── FileActivity.java │ │ │ ├── FileUntil.java │ │ │ ├── FileViewAdapter.java │ │ │ ├── GData.java │ │ │ ├── JSEngine.java │ │ │ ├── MainActivity.java │ │ │ ├── ModDemo.java │ │ │ ├── ModViewAdapter.java │ │ │ ├── SelectActivity.java │ │ │ ├── SettingActivity.java │ │ │ ├── TransService.java │ │ │ └── loader │ │ │ ├── ModLoadingProtocolServer.java │ │ │ └── protocol │ │ │ ├── ProtocolException.java │ │ │ ├── format │ │ │ ├── FetchModsPacket.java │ │ │ ├── FetchModsResponsePacket.java │ │ │ ├── FetchRuntimePacket.java │ │ │ ├── FetchRuntimeResponsePacket.java │ │ │ ├── JSONUtils.java │ │ │ └── Packet.java │ │ │ ├── package-info.java │ │ │ └── server │ │ │ ├── LoadingWebsocketProtocolServer.java │ │ │ ├── NeutronProtocolServer.java │ │ │ └── ProtocolRequestHandler.java │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── file.png │ │ ├── folder.png │ │ ├── ic_launcher_background.xml │ │ └── pack.png │ │ ├── layout │ │ ├── activity_about.xml │ │ ├── activity_file.xml │ │ ├── activity_main.xml │ │ ├── activity_select.xml │ │ ├── activity_setting.xml │ │ ├── alert.xml │ │ ├── file_row.xml │ │ └── mod_row.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ │ └── xml │ │ └── filepaths.xml │ └── test │ └── java │ └── com │ └── mjtg │ └── neutron │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── readme.md ├── runtime ├── .gitignore ├── api │ ├── .gitignore │ ├── build.gradle │ └── src │ │ └── main │ │ └── java │ │ └── com │ │ └── mjtg │ │ └── neutron │ │ └── api │ │ ├── NeutronMod.java │ │ └── hooks │ │ ├── ItemStack.java │ │ └── NeutronHooks.java ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── local.properties ├── preloading │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── cpp │ │ ├── CMakeLists.txt │ │ └── stub.cpp │ │ └── java │ │ └── com │ │ └── mjtg │ │ └── neutron │ │ └── runtime │ │ ├── hook │ │ └── PESORegistry.java │ │ ├── loader │ │ └── protocol │ │ │ ├── ProtocolException.java │ │ │ ├── client │ │ │ ├── NeutronProtocolClient.java │ │ │ └── NeutronWebSocketClient.java │ │ │ ├── format │ │ │ ├── FetchModsPacket.java │ │ │ ├── FetchModsResponsePacket.java │ │ │ ├── FetchRuntimePacket.java │ │ │ ├── FetchRuntimeResponsePacket.java │ │ │ ├── JSONUtils.java │ │ │ └── Packet.java │ │ │ └── package-info.java │ │ └── mixin │ │ └── MCMainActivityMixin.java ├── running │ ├── .gitignore │ ├── build.gradle │ ├── consumer-rules.pro │ ├── proguard-rules.pro │ └── src │ │ ├── androidTest │ │ └── java │ │ │ └── com │ │ │ └── mjtg │ │ │ └── neutron │ │ │ └── patcher │ │ │ └── ExampleInstrumentedTest.java │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── cpp │ │ │ ├── CMakeLists.txt │ │ │ ├── dummy.cpp │ │ │ ├── libs │ │ │ │ ├── libsubstrate-dvm.so │ │ │ │ └── libsubstrate.so │ │ │ ├── neutron.cy.cpp │ │ │ └── substrate.h │ │ ├── java │ │ │ └── com │ │ │ │ └── mjtg │ │ │ │ └── neutron │ │ │ │ ├── hook │ │ │ │ └── NeutronHooking.java │ │ │ │ └── runtime │ │ │ │ └── NeutronRuntime.java │ │ └── res │ │ │ ├── values │ │ │ └── strings.xml │ │ │ └── xml │ │ │ └── filepaths.xml │ │ └── test │ │ └── java │ │ └── com │ │ └── mjtg │ │ └── neutron │ │ └── patcher │ │ └── ExampleUnitTest.java └── settings.gradle ├── settings.gradle └── tools ├── .gitignore ├── README.md ├── build.gradle ├── libs ├── dex2jar │ ├── antlr-runtime-3.5.jar │ ├── asm-debug-all-4.1.jar │ ├── d2j-base-cmd-2.0.jar │ ├── d2j-jasmin-2.0.jar │ ├── d2j-smali-2.0.jar │ ├── dex-ir-2.0.jar │ ├── dex-reader-2.0.jar │ ├── dex-reader-api-2.0.jar │ ├── dex-tools-2.0.jar │ ├── dex-translator-2.0.jar │ ├── dex-writer-2.0.jar │ └── dx-1.7.jar └── javaassist │ └── javassist-3.26.0.jar └── src └── main └── java ├── android └── os │ └── Bundle.java └── com └── mjtg └── neutron ├── cli └── Main.java ├── converter └── DexJarConverter.java ├── packer └── JarPacker.java ├── patcher ├── ApkPatcher.java ├── DexPatcher.java ├── PatchApkFromRuntimeAARCommand.java └── PrepareRuntimeDirFromAAR.java └── util ├── PathUtil.java └── ZipUtil.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | .cxx 4 | /local.properties 5 | /.idea/libraries 6 | /.idea/caches 7 | /.idea/modules.xml 8 | /.idea/workspace.xml 9 | .DS_Store 10 | /build 11 | /captures 12 | .externalNativeBuild 13 | .idea 14 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeutronLauncher-Dev/Neutron/49b205768f370de26a113cc430f5e5cc8c643e0a/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 |
7 | 8 | 9 | 10 | xmlns:android 11 | 12 | ^$ 13 | 14 | 15 | 16 |
17 |
18 | 19 | 20 | 21 | xmlns:.* 22 | 23 | ^$ 24 | 25 | 26 | BY_NAME 27 | 28 |
29 |
30 | 31 | 32 | 33 | .*:id 34 | 35 | http://schemas.android.com/apk/res/android 36 | 37 | 38 | 39 |
40 |
41 | 42 | 43 | 44 | .*:name 45 | 46 | http://schemas.android.com/apk/res/android 47 | 48 | 49 | 50 |
51 |
52 | 53 | 54 | 55 | name 56 | 57 | ^$ 58 | 59 | 60 | 61 |
62 |
63 | 64 | 65 | 66 | style 67 | 68 | ^$ 69 | 70 | 71 | 72 |
73 |
74 | 75 | 76 | 77 | .* 78 | 79 | ^$ 80 | 81 | 82 | BY_NAME 83 | 84 |
85 |
86 | 87 | 88 | 89 | .* 90 | 91 | http://schemas.android.com/apk/res/android 92 | 93 | 94 | 95 |
96 |
97 | 98 | 99 | 100 | .* 101 | 102 | .* 103 | 104 | 105 | BY_NAME 106 | 107 |
108 |
109 |
110 |
111 |
112 |
-------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 20 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 41 | 42 | 43 | 44 | 45 | 46 | 48 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | defaultConfig { 6 | applicationId "com.mjtg.neutron" 7 | minSdkVersion 24 8 | targetSdkVersion 28 9 | versionCode 1 10 | versionName "1.0" 11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 12 | } 13 | buildTypes { 14 | release { 15 | minifyEnabled false 16 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 17 | } 18 | } 19 | compileOptions { 20 | sourceCompatibility = 1.8 21 | targetCompatibility = 1.8 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(include: ['*.jar'], dir: 'libs') 27 | implementation 'org.java-websocket:Java-WebSocket:1.4.0' 28 | implementation 'com.google.guava:guava:28.2-jre' 29 | implementation 'com.android.support:appcompat-v7:28.0.0' 30 | implementation 'com.android.support.constraint:constraint-layout:1.1.3' 31 | implementation 'com.android.support:design:28.0.0' 32 | testImplementation 'junit:junit:4.12' 33 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 34 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 35 | implementation 'com.android.support:gridlayout-v7:28.0.0' 36 | implementation 'com.github.tbruyelle:rxpermissions:0.10.2' 37 | implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' 38 | implementation 'io.reactivex.rxjava2:rxjava:2.2.4' 39 | implementation files('libs/json-20190722.jar') 40 | implementation files('libs/rhino-1.7.12.jar') 41 | } 42 | -------------------------------------------------------------------------------- /app/libs/json-20190722.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeutronLauncher-Dev/Neutron/49b205768f370de26a113cc430f5e5cc8c643e0a/app/libs/json-20190722.jar -------------------------------------------------------------------------------- /app/libs/rhino-1.7.12.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeutronLauncher-Dev/Neutron/49b205768f370de26a113cc430f5e5cc8c643e0a/app/libs/rhino-1.7.12.jar -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/mjtg/neutron/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("com.mjtg.neutron", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 13 | 17 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/assets/classes.dex: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeutronLauncher-Dev/Neutron/49b205768f370de26a113cc430f5e5cc8c643e0a/app/src/main/assets/classes.dex -------------------------------------------------------------------------------- /app/src/main/assets/pre.js: -------------------------------------------------------------------------------- 1 | function EXPORT(func){ 2 | 3 | } -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.os.Bundle; 4 | import android.support.annotation.Nullable; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.webkit.WebView; 7 | 8 | public class AboutActivity extends AppCompatActivity { 9 | @Override 10 | protected void onCreate(@Nullable Bundle savedInstanceState) { 11 | super.onCreate(savedInstanceState); 12 | setContentView(R.layout.activity_about); 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/FileActivity.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.os.Bundle; 4 | import android.os.Environment; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.widget.ListView; 8 | import android.widget.TextView; 9 | 10 | import java.io.File; 11 | import java.lang.ref.WeakReference; 12 | 13 | public class FileActivity extends AppCompatActivity { 14 | public File dir; 15 | ListView list; 16 | TextView pathView; 17 | @Override 18 | protected void onCreate(@Nullable Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_file); 21 | 22 | GData.fileActivity=new WeakReference<>(this); 23 | 24 | dir= Environment.getExternalStorageDirectory(); 25 | list=findViewById(R.id.file_list); 26 | pathView=findViewById(R.id.path); 27 | 28 | toPath(); 29 | } 30 | public void toPath(){ 31 | FileViewAdapter fva=new FileViewAdapter(this,dir.getPath()); 32 | list.setAdapter(fva); 33 | pathView.setText(dir.getPath()); 34 | } 35 | public void upPath(){ 36 | dir=dir.getParentFile(); 37 | toPath(); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/FileUntil.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.content.Context; 4 | import android.os.Build; 5 | import android.support.annotation.RequiresApi; 6 | 7 | import java.io.BufferedOutputStream; 8 | import java.io.BufferedReader; 9 | import java.io.File; 10 | import java.io.FileInputStream; 11 | import java.io.FileNotFoundException; 12 | import java.io.FileOutputStream; 13 | import java.io.FileReader; 14 | import java.io.IOException; 15 | import java.io.InputStream; 16 | import java.io.OutputStream; 17 | import java.nio.file.Path; 18 | import java.util.Enumeration; 19 | import java.util.zip.ZipEntry; 20 | import java.util.zip.ZipException; 21 | import java.util.zip.ZipFile; 22 | import java.util.zip.ZipInputStream; 23 | import java.util.zip.ZipOutputStream; 24 | 25 | 26 | /** 27 | * 28 | * @version 1.0 29 | * @since 2015-9-11 30 | * @category com.feng.util 31 | * 32 | */ 33 | public final class FileUntil 34 | { 35 | public static void zipUncompress(String inputFile, String destDirPath) throws IOException { 36 | File srcFile = new File(inputFile);//获取当前压缩文件 37 | // 判断源文件是否存在 38 | if (!srcFile.exists()) { 39 | throw new IOException(srcFile.getPath() + "所指文件不存在"); 40 | } 41 | //开始解压 42 | //构建解压输入流 43 | ZipInputStream zIn = new ZipInputStream(new FileInputStream(srcFile)); 44 | ZipEntry entry = null; 45 | File file = null; 46 | while ((entry = zIn.getNextEntry()) != null) { 47 | if (!entry.isDirectory()) { 48 | file = new File(destDirPath, entry.getName()); 49 | if (!file.exists()) { 50 | new File(file.getParent()).mkdirs();//创建此文件的上级目录 51 | } 52 | OutputStream out = new FileOutputStream(file); 53 | BufferedOutputStream bos = new BufferedOutputStream(out); 54 | int len = -1; 55 | byte[] buf = new byte[1024]; 56 | while ((len = zIn.read(buf)) != -1) { 57 | bos.write(buf, 0, len); 58 | } 59 | // 关流顺序,先打开的后关闭 60 | bos.close(); 61 | out.close(); 62 | } 63 | } 64 | } 65 | public static String readAssets(Context context, String fileName){ 66 | try { 67 | InputStream is = context.getAssets().open(fileName); 68 | int size = is.available(); 69 | byte[] buffer = new byte[size]; 70 | is.read(buffer); 71 | is.close(); 72 | return new String(buffer, "utf-8"); 73 | } catch (IOException e) { 74 | e.printStackTrace(); 75 | } 76 | return null; 77 | } 78 | public static String readFile(String path){ 79 | StringBuilder res=new StringBuilder(); 80 | try { 81 | FileReader reader = new FileReader(new File(path)); 82 | BufferedReader bufferedReader=new BufferedReader(reader); 83 | String line; 84 | while((line=bufferedReader.readLine())!=null){ 85 | res.append(line); 86 | } 87 | bufferedReader.close(); 88 | reader.close(); 89 | }catch (IOException e){ 90 | e.printStackTrace(); 91 | } 92 | return res.toString(); 93 | } 94 | public static boolean deletefile(String path){ 95 | boolean flag = false; 96 | File file = new File(path); 97 | if (!file.exists()) { 98 | return false; 99 | } 100 | if (!file.isDirectory()) { 101 | return false; 102 | } 103 | String[] str = file.list(); 104 | for (int i = 0; i < str.length; i++) { 105 | System.out.println("333:"+str[i]); 106 | File fi = new File(path + "/" + str[i]); 107 | if (path.endsWith(File.separator)) { 108 | fi = new File(path + str[i]); 109 | } else { 110 | fi = new File(path + File.separator + str[i]); 111 | } 112 | 113 | if(fi.exists()||fi.list().length==0){ 114 | File myFilePath = new File(path+"/"+str[i]); 115 | myFilePath.delete(); 116 | } 117 | if(fi.isDirectory())//如果文件假内还有 就继续调用本方法 118 | { 119 | deletefile(path+"/"+str[i]); 120 | }else{ 121 | fi.delete(); 122 | } 123 | 124 | } 125 | return true; 126 | } 127 | 128 | 129 | } 130 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/FileViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import java.io.File; 13 | 14 | public class FileViewAdapter extends BaseAdapter { 15 | File dir; 16 | Context ctx; 17 | public FileViewAdapter(Context ctx,String path){ 18 | this.ctx=ctx; 19 | dir=new File(path); 20 | } 21 | @Override 22 | public int getCount() { 23 | return dir.list().length; 24 | } 25 | 26 | @Override 27 | public Object getItem(int i) { 28 | return null; 29 | } 30 | 31 | @Override 32 | public long getItemId(int i) { 33 | return 0; 34 | } 35 | 36 | @Override 37 | @SuppressLint("ViewHolder") 38 | public View getView(final int i, View view, ViewGroup viewGroup) { 39 | final File[] files=dir.listFiles(); 40 | 41 | final FileActivity fa= GData.fileActivity.get(); 42 | final SelectActivity sa=GData.selectActivity.get(); 43 | View v= LayoutInflater.from(ctx).inflate(R.layout.file_row,null); 44 | final TextView tv=v.findViewById(R.id.name); 45 | ImageView icon=v.findViewById(R.id.icon); 46 | tv.setText(files[i].getName()); 47 | 48 | if(files[i].isDirectory()) { 49 | icon.setImageResource(R.drawable.folder); 50 | v.setOnClickListener(new View.OnClickListener() { 51 | @Override 52 | public void onClick(View view) { 53 | fa.dir=files[i]; 54 | fa.toPath(); 55 | } 56 | }); 57 | } 58 | else{ 59 | String[] ss=files[i].getName().split("\\."); 60 | String s=ss[ss.length-1]; 61 | if(s.equals("neu")) { 62 | icon.setImageResource(R.drawable.pack); 63 | GData.showAlert(ctx, fa.getWindow().getDecorView(), "你确定添加此模组吗", new View.OnClickListener() { 64 | @Override 65 | public void onClick(View view) { 66 | sa.addMod(GData.decodeModFromPath(files[i].getPath())); 67 | } 68 | }); 69 | } 70 | else 71 | icon.setImageResource(R.drawable.file); 72 | } 73 | return v; 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/GData.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.content.Context; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.Color; 7 | import android.graphics.drawable.ColorDrawable; 8 | import android.view.Display; 9 | import android.view.Gravity; 10 | import android.view.LayoutInflater; 11 | import android.view.View; 12 | import android.widget.PopupWindow; 13 | import android.widget.TextView; 14 | 15 | import org.json.JSONException; 16 | import org.json.JSONObject; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.lang.ref.WeakReference; 21 | import java.util.ArrayList; 22 | 23 | public class GData { 24 | public static ArrayList modDemoList=new ArrayList<>(); 25 | public static WeakReference selectActivity; 26 | public static WeakReference fileActivity; 27 | 28 | public static void showAlert(Context ctx,View parent,String content, View.OnClickListener ifyes){ 29 | PopupWindow pw=new PopupWindow(ctx); 30 | View v= LayoutInflater.from(ctx).inflate(R.layout.alert,null); 31 | TextView contentText=v.findViewById(R.id.content), 32 | yesText=v.findViewById(R.id.yes); 33 | contentText.setText(content); 34 | yesText.setOnClickListener(ifyes); 35 | 36 | pw.setWidth(-2); 37 | pw.setHeight(-2); 38 | pw.setFocusable(false); 39 | pw.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT)); 40 | pw.showAtLocation(parent, Gravity.CENTER,0,0); 41 | pw.setContentView(v); 42 | } 43 | public static ModDemo decodeModFromPath(String path) { 44 | ModDemo md=new ModDemo(); 45 | String introduction="F2L"; 46 | String name="F2L"; 47 | String s=File.separator; 48 | File file=new File(path); 49 | md.path=path; 50 | try { 51 | FileUntil.zipUncompress(path,file.getParent()+s+"temp"+s); 52 | } catch (IOException e) { 53 | e.printStackTrace(); 54 | } 55 | md.icon= BitmapFactory.decodeFile(file.getParent()+s+"temp"+s+"icon.png"); 56 | try { 57 | JSONObject json=new JSONObject(FileUntil.readFile(file.getParent()+s+"temp"+s+"info.json")); 58 | introduction=json.getString("introduction"); 59 | name=json.getString("name"); 60 | } catch (JSONException e) { 61 | e.printStackTrace(); 62 | } 63 | md.introduction=introduction; 64 | md.name=name; 65 | FileUntil.deletefile(file.getParent()+s+"temp"); 66 | return md; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/JSEngine.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | 4 | import android.content.Context; 5 | 6 | import org.mozilla.javascript.Scriptable; 7 | import org.mozilla.javascript.ScriptableObject; 8 | 9 | import java.io.File; 10 | 11 | public class JSEngine { 12 | private Class clazz; 13 | private String allFunctions = "";//js方法语句 14 | private Context ctx; 15 | public JSEngine(Context ctx) { 16 | this.clazz = JSEngine.class; 17 | initJSStr("pre.js");//初始化js语句 18 | } 19 | 20 | private void initJSStr(String filename) { 21 | this.ctx=ctx; 22 | allFunctions = " var ScriptAPI = java.lang.Class.forName(\"" + JSEngine.class.getName() + "\", true, javaLoader);\n"+FileUntil.readAssets(ctx,filename); 23 | } 24 | 25 | //本地java方法 26 | public void setValue(Object keyStr, Object o) { 27 | System.out.println("JSEngine output - setValue : " + keyStr.toString() + " ------> " + o.toString()); 28 | } 29 | 30 | //本地java方法 31 | public String getValue(String keyStr) { 32 | System.out.println("JSEngine output - getValue : " + keyStr.toString()); 33 | return "获取到值了"; 34 | } 35 | 36 | public void runScript(String js) { 37 | String runJSStr = allFunctions + "\n" + js;//运行js = allFunctions + js 38 | org.mozilla.javascript.Context rhino = org.mozilla.javascript.Context.enter(); 39 | rhino.setOptimizationLevel(-1) ; 40 | try { 41 | Scriptable scope = rhino.initStandardObjects(); 42 | 43 | ScriptableObject.putProperty(scope, "javaContext", org.mozilla.javascript.Context.javaToJS(this, scope));//配置属性 javaContext:当前类JSEngine的上下文 44 | ScriptableObject.putProperty(scope, "javaLoader", org.mozilla.javascript.Context.javaToJS(clazz.getClassLoader(), scope));//配置属性 javaLoader:当前类的JSEngine的类加载器 45 | 46 | rhino.evaluateString(scope, runJSStr, clazz.getSimpleName(), 1, null); 47 | } finally { 48 | org.mozilla.javascript.Context.exit(); 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.Manifest; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.support.design.widget.FloatingActionButton; 7 | import android.support.design.widget.Snackbar; 8 | import android.support.v7.app.AppCompatActivity; 9 | import android.support.v7.widget.CardView; 10 | import android.support.v7.widget.Toolbar; 11 | import android.view.View; 12 | import android.widget.Button; 13 | 14 | import com.tbruyelle.rxpermissions2.RxPermissions; 15 | 16 | import java.util.function.Consumer; 17 | 18 | public class MainActivity extends AppCompatActivity { 19 | Button card_manage,card_launch,card_settings,card_shop; 20 | private String[] permissionsGroup = new String[]{ 21 | Manifest.permission.READ_EXTERNAL_STORAGE, 22 | Manifest.permission.WRITE_EXTERNAL_STORAGE}; 23 | @Override 24 | protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | setContentView(R.layout.activity_main); 27 | 28 | startService(new Intent(this,TransService.class)); 29 | 30 | card_manage=findViewById(R.id.button_manage); 31 | card_launch=findViewById(R.id.button_init); 32 | card_settings=findViewById(R.id.button_set); 33 | card_shop=findViewById(R.id.button_shop); 34 | 35 | card_manage.setOnClickListener(new View.OnClickListener() { 36 | @Override 37 | public void onClick(View view) { 38 | Intent intent=new Intent(MainActivity.this,SelectActivity.class); 39 | startActivity(intent); 40 | } 41 | }); 42 | RxPermissions permissions=new RxPermissions(this); 43 | permissions.request(permissionsGroup); 44 | 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/ModDemo.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.graphics.Bitmap; 4 | 5 | public class ModDemo { 6 | public String path; 7 | public String name; 8 | public String introduction; 9 | public Bitmap icon; 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/ModViewAdapter.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.annotation.SuppressLint; 4 | import android.content.Context; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.BaseAdapter; 9 | import android.widget.ImageView; 10 | import android.widget.TextView; 11 | 12 | import org.w3c.dom.Text; 13 | 14 | import java.util.ArrayList; 15 | 16 | public class ModViewAdapter extends BaseAdapter { 17 | Context ctx; 18 | ArrayList list; 19 | public ModViewAdapter(Context ctx,ArrayList list){ 20 | this.ctx=ctx; 21 | 22 | } 23 | @Override 24 | public int getCount() { 25 | return list.size(); 26 | } 27 | 28 | @Override 29 | public Object getItem(int i) { 30 | return null; 31 | } 32 | 33 | @Override 34 | public long getItemId(int i) { 35 | return 0; 36 | } 37 | 38 | @Override 39 | @SuppressLint("ViewHolder") 40 | public View getView(int i, View view, ViewGroup viewGroup) { 41 | View v= LayoutInflater.from(ctx).inflate(R.layout.mod_row,null); 42 | ImageView icon=v.findViewById(R.id.icon); 43 | TextView name=v.findViewById(R.id.name); 44 | TextView introduction=v.findViewById(R.id.introduction); 45 | icon.setImageBitmap(list.get(i).icon); 46 | name.setText(list.get(i).name); 47 | introduction.setText(list.get(i).introduction); 48 | return v; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/SelectActivity.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.view.View; 8 | import android.widget.ListView; 9 | import android.widget.TextView; 10 | 11 | import java.lang.ref.WeakReference; 12 | 13 | public class SelectActivity extends AppCompatActivity { 14 | 15 | ListView list; 16 | TextView addModText; 17 | @Override 18 | protected void onCreate(@Nullable Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_select); 21 | 22 | GData.selectActivity=new WeakReference<>(this); 23 | 24 | list=findViewById(R.id.mod_list); 25 | addModText=findViewById(R.id.add_mod); 26 | 27 | addModText.setOnClickListener(new View.OnClickListener() { 28 | @Override 29 | public void onClick(View view) { 30 | Intent intent=new Intent(SelectActivity.this,FileActivity.class); 31 | startActivity(intent); 32 | } 33 | }); 34 | 35 | updateList(); 36 | 37 | } 38 | public void addMod(ModDemo md){ 39 | GData.modDemoList.add(0,md); 40 | updateList(); 41 | } 42 | public void removeMod(int index){ 43 | GData.modDemoList.remove(index); 44 | updateList(); 45 | 46 | } 47 | public void updateList(){ 48 | list.setAdapter(new ModViewAdapter(this,GData.modDemoList)); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/SettingActivity.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.content.Intent; 4 | import android.os.Bundle; 5 | import android.support.annotation.Nullable; 6 | import android.support.v7.app.AppCompatActivity; 7 | import android.support.v7.widget.CardView; 8 | import android.view.View; 9 | 10 | public class SettingActivity extends AppCompatActivity { 11 | @Override 12 | protected void onCreate(@Nullable Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_setting); 15 | 16 | CardView cv_about=findViewById(R.id.button_about); 17 | 18 | cv_about.setOnClickListener(new View.OnClickListener() { 19 | @Override 20 | public void onClick(View view) { 21 | startActivity(new Intent(SettingActivity.this,AboutActivity.class)); 22 | } 23 | }); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/TransService.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron; 2 | 3 | import android.app.Service; 4 | import android.content.Intent; 5 | import android.os.Handler; 6 | import android.os.IBinder; 7 | import android.support.annotation.Nullable; 8 | import android.util.Log; 9 | 10 | import com.google.common.collect.Lists; 11 | import com.mjtg.neutron.loader.ModLoadingProtocolServer; 12 | import com.mjtg.neutron.loader.protocol.server.NeutronProtocolServer; 13 | 14 | import java.io.File; 15 | import java.io.FileInputStream; 16 | import java.io.FileNotFoundException; 17 | import java.io.IOException; 18 | import java.io.InputStream; 19 | import java.nio.ByteBuffer; 20 | import java.nio.file.Paths; 21 | 22 | public class TransService extends Service { 23 | 24 | private NeutronProtocolServer server; 25 | 26 | @Nullable 27 | @Override 28 | public IBinder onBind(Intent intent) { 29 | return null; 30 | } 31 | 32 | 33 | @Override 34 | public void onCreate() { 35 | super.onCreate(); 36 | } 37 | 38 | @Override 39 | public int onStartCommand(Intent intent, int flags, int startId) { 40 | Log.d("Neutron-ModServer", "starting protocol server"); 41 | server = new NeutronProtocolServer(this); 42 | server.start(); 43 | return super.onStartCommand(intent, flags, startId); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/ModLoadingProtocolServer.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.common.io.ByteStreams; 6 | 7 | import org.java_websocket.WebSocket; 8 | import org.java_websocket.handshake.ClientHandshake; 9 | import org.java_websocket.server.WebSocketServer; 10 | import org.json.JSONArray; 11 | import org.json.JSONException; 12 | import org.json.JSONObject; 13 | 14 | import java.io.BufferedInputStream; 15 | import java.io.FileInputStream; 16 | import java.io.IOException; 17 | import java.io.InputStream; 18 | import java.net.InetSocketAddress; 19 | import java.nio.ByteBuffer; 20 | import java.nio.file.Path; 21 | import java.util.List; 22 | import java.util.function.Function; 23 | import java.util.function.Supplier; 24 | 25 | public class ModLoadingProtocolServer extends WebSocketServer { 26 | 27 | //a function that returns a list of mods to load 28 | private Supplier> fetchModList; 29 | //a function that turns a mod name into a path to it's jar, return null if the mod not exists 30 | private Function fetchMod; 31 | 32 | public ModLoadingProtocolServer(Supplier> fetchModList, Function fetchMod) { 33 | super( new InetSocketAddress( 32770 ) ); 34 | this.fetchModList = fetchModList; 35 | this.fetchMod = fetchMod; 36 | } 37 | 38 | @Override 39 | public void onOpen(WebSocket conn, ClientHandshake handshake) {} 40 | 41 | @Override 42 | public void onClose(WebSocket conn, int code, String reason, boolean remote) {} 43 | 44 | @Override 45 | public void onMessage( WebSocket conn, String message ) { 46 | Log.d("Neutron-ModServer", "onMessage: received message from client: "+message); 47 | try { 48 | final JSONObject json = new JSONObject(message); 49 | switch(json.getString("type")) { 50 | case "listMods": 51 | handleListMods(conn, json); 52 | break; 53 | case "fetchMod": 54 | handleFetchMod(conn, json); 55 | break; 56 | } 57 | } catch (JSONException e) { 58 | Log.w("Neutron-ModServer", "onMessage: received invalid message from client: "+message, e); 59 | conn.close(); 60 | } 61 | } 62 | 63 | private void handleFetchMod(WebSocket conn, JSONObject json) throws JSONException { 64 | final String name = json.getString("modName"); 65 | try(InputStream path = fetchMod.apply(name)) { 66 | final byte[] read = ByteStreams.toByteArray(path); 67 | conn.send(read); 68 | } catch (IOException e) { 69 | Log.w("Neutron-ModServer", "onMessage: unable to read name: "+name, e); 70 | conn.close(); 71 | } 72 | } 73 | 74 | @Override 75 | public void onMessage( WebSocket conn, ByteBuffer message ) { 76 | Log.w("Neutron-ModServer", "onMessage: received invalid message from client: "+message); 77 | conn.close(); 78 | } 79 | 80 | @Override 81 | public void onError( WebSocket conn, Exception ex ) { 82 | Log.w("Neutron-ModServer", "onError: error trying to start Mod Server!", ex); 83 | if( conn != null ) { 84 | // some errors like port binding failed may not be assignable to a specific websocket 85 | } 86 | } 87 | 88 | @Override 89 | public void onStart() { 90 | Log.d("Neutron-ModServer", "server is now up and running!"); 91 | setConnectionLostTimeout(0); 92 | setConnectionLostTimeout(100); 93 | } 94 | 95 | private void handleListMods(WebSocket conn, JSONObject json) { 96 | conn.send(new JSONArray(fetchModList.get()).toString()); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/ProtocolException.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol; 2 | 3 | public class ProtocolException extends RuntimeException { 4 | 5 | public ProtocolException() { 6 | super(); 7 | } 8 | 9 | public ProtocolException(String message) { 10 | super(message); 11 | } 12 | 13 | 14 | public ProtocolException(String message, Throwable cause) { 15 | super(message, cause); 16 | } 17 | 18 | 19 | public ProtocolException(Throwable cause) { 20 | super(cause); 21 | } 22 | 23 | 24 | protected ProtocolException(String message, Throwable cause, 25 | boolean enableSuppression, 26 | boolean writableStackTrace) { 27 | super(message, cause, enableSuppression, writableStackTrace); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/format/FetchModsPacket.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.format; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | public class FetchModsPacket extends Packet { 7 | 8 | public static final String TYPE = "fetchMods"; 9 | 10 | 11 | @Override 12 | public String getType() { 13 | return TYPE; 14 | } 15 | 16 | public JSONObject toJson() { 17 | try { 18 | return new JSONObject() 19 | .put("type", TYPE) 20 | ; 21 | } catch (JSONException e) { 22 | throw new RuntimeException(e); 23 | } 24 | } 25 | 26 | public static FetchModsPacket fromJson(JSONObject obj) { 27 | try { 28 | if(obj.getString("type").equals(TYPE)) { 29 | return new FetchModsPacket(); 30 | } else { 31 | throw new IllegalArgumentException("incorrect type"); 32 | } 33 | } catch (JSONException e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/format/FetchModsResponsePacket.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.format; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class FetchModsResponsePacket extends Packet { 10 | 11 | public static final String TYPE = "fetchModsResponsePacket"; 12 | 13 | public Map mods = new HashMap<>(); 14 | 15 | 16 | @Override 17 | public String getType() { 18 | return TYPE; 19 | } 20 | 21 | public JSONObject toJson() { 22 | try { 23 | return new JSONObject() 24 | .put("type", TYPE) 25 | .put("mods", JSONUtils.encodeAsBase64Object(mods)) 26 | ; 27 | } catch (JSONException e) { 28 | throw new RuntimeException(e); 29 | } 30 | } 31 | 32 | public static FetchModsResponsePacket fromJson(JSONObject obj) { 33 | try { 34 | if(obj.getString("type").equals(TYPE)) { 35 | final FetchModsResponsePacket result = new FetchModsResponsePacket(); 36 | result.mods = JSONUtils.decodeAsBase64Object(obj.getJSONObject("mods")); 37 | return result; 38 | } else { 39 | throw new IllegalArgumentException("incorrect type"); 40 | } 41 | } catch (JSONException e) { 42 | throw new RuntimeException(e); 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/format/FetchRuntimePacket.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.format; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | public class FetchRuntimePacket extends Packet { 7 | 8 | public static final String TYPE = "fetchRuntime"; 9 | 10 | 11 | @Override 12 | public String getType() { 13 | return TYPE; 14 | } 15 | 16 | public JSONObject toJson() { 17 | try { 18 | return new JSONObject() 19 | .put("type", TYPE) 20 | ; 21 | } catch (JSONException e) { 22 | throw new RuntimeException(e); 23 | } 24 | } 25 | 26 | public static FetchRuntimePacket fromJson(JSONObject obj) { 27 | try { 28 | if(obj.getString("type").equals(TYPE)) { 29 | return new FetchRuntimePacket(); 30 | } else { 31 | throw new IllegalArgumentException("incorrect type"); 32 | } 33 | } catch (JSONException e) { 34 | throw new RuntimeException(e); 35 | } 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/format/FetchRuntimeResponsePacket.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.format; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | public class FetchRuntimeResponsePacket extends Packet { 10 | 11 | public static final String TYPE = "fetchRuntimeResponse"; 12 | 13 | //map of libname-binary value 14 | public Map nativeLibraries = new HashMap<>(); 15 | 16 | //map of dexname-binary value 17 | public Map runtimeJars = new HashMap<>(); 18 | 19 | 20 | @Override 21 | public String getType() { 22 | return TYPE; 23 | } 24 | 25 | public JSONObject toJson() { 26 | try { 27 | return new JSONObject() 28 | .put("type", TYPE) 29 | .put("nativeLibs", JSONUtils.encodeAsBase64Object(nativeLibraries)) 30 | .put("jars", JSONUtils.encodeAsBase64Object(runtimeJars)) 31 | ; 32 | } catch (JSONException e) { 33 | throw new RuntimeException(e); 34 | } 35 | } 36 | 37 | public static FetchRuntimeResponsePacket fromJson(JSONObject obj) { 38 | try { 39 | if(obj.getString("type").equals(TYPE)) { 40 | final FetchRuntimeResponsePacket result = new FetchRuntimeResponsePacket(); 41 | result.nativeLibraries = JSONUtils.decodeAsBase64Object(obj.getJSONObject("nativeLibs")); 42 | result.runtimeJars = JSONUtils.decodeAsBase64Object(obj.getJSONObject("jars")); 43 | return result; 44 | } else { 45 | throw new IllegalArgumentException("incorrect type"); 46 | } 47 | } catch (JSONException e) { 48 | throw new RuntimeException(e); 49 | } 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/format/JSONUtils.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.format; 2 | 3 | import android.util.Base64; 4 | 5 | import org.json.JSONException; 6 | import org.json.JSONObject; 7 | 8 | import java.util.HashMap; 9 | import java.util.Iterator; 10 | import java.util.Map; 11 | 12 | public class JSONUtils { 13 | 14 | public static JSONObject encodeAsBase64Object(Map map) throws JSONException { 15 | JSONObject object = new JSONObject(); 16 | for (Map.Entry entry : map.entrySet()) { 17 | object.put( 18 | entry.getKey(), 19 | Base64.encodeToString(entry.getValue(), Base64.DEFAULT) 20 | ); 21 | } 22 | return object; 23 | } 24 | 25 | public static Map decodeAsBase64Object(JSONObject obj) throws JSONException { 26 | Map map = new HashMap<>(); 27 | final Iterator iter = obj.keys(); 28 | while (iter.hasNext()) { 29 | final String key = iter.next(); 30 | map.put( 31 | key, 32 | Base64.decode(obj.getString(key), Base64.DEFAULT) 33 | ); 34 | } 35 | return map; 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/format/Packet.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.format; 2 | 3 | import org.json.JSONException; 4 | import org.json.JSONObject; 5 | 6 | public abstract class Packet { 7 | 8 | public abstract String getType(); 9 | 10 | public abstract JSONObject toJson(); 11 | 12 | public static Packet fromJson(JSONObject obj) { 13 | try { 14 | switch(obj.getString("type")) { 15 | case FetchModsPacket.TYPE: 16 | return FetchModsPacket.fromJson(obj); 17 | case FetchModsResponsePacket.TYPE: 18 | return FetchModsResponsePacket.fromJson(obj); 19 | case FetchRuntimePacket.TYPE: 20 | return FetchRuntimePacket.fromJson(obj); 21 | case FetchRuntimeResponsePacket.TYPE: 22 | return FetchRuntimeResponsePacket.fromJson(obj); 23 | default: 24 | throw new IllegalArgumentException("invalid type!"); 25 | } 26 | } catch (JSONException e) { 27 | throw new RuntimeException(e); 28 | } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/package-info.java: -------------------------------------------------------------------------------- 1 | /** 2 | * the package that contains all the protocols needed to communicate with launcher 3 | * to download runtime and mods 4 | * 5 | * use client.NeutronProtocolClient to communicate! 6 | **/ 7 | package com.mjtg.neutron.loader.protocol; -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/server/LoadingWebsocketProtocolServer.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.server; 2 | 3 | import android.util.Log; 4 | 5 | import com.google.common.io.Files; 6 | import com.mjtg.neutron.loader.protocol.format.FetchModsPacket; 7 | import com.mjtg.neutron.loader.protocol.format.FetchModsResponsePacket; 8 | import com.mjtg.neutron.loader.protocol.format.FetchRuntimePacket; 9 | import com.mjtg.neutron.loader.protocol.format.FetchRuntimeResponsePacket; 10 | import com.mjtg.neutron.loader.protocol.format.Packet; 11 | 12 | import org.java_websocket.WebSocket; 13 | import org.java_websocket.handshake.ClientHandshake; 14 | import org.java_websocket.server.WebSocketServer; 15 | import org.json.JSONException; 16 | import org.json.JSONObject; 17 | 18 | import java.io.File; 19 | import java.io.IOException; 20 | import java.net.InetSocketAddress; 21 | import java.nio.ByteBuffer; 22 | import java.util.Map; 23 | 24 | public class LoadingWebsocketProtocolServer extends WebSocketServer { 25 | 26 | private static final String TAG = "Neutron-ModServer"; 27 | 28 | private ProtocolRequestHandler handler; 29 | 30 | 31 | public LoadingWebsocketProtocolServer(int port, ProtocolRequestHandler handler) { 32 | super( new InetSocketAddress( port ) ); 33 | this.handler = handler; 34 | } 35 | 36 | @Override 37 | public void onOpen(WebSocket conn, ClientHandshake handshake) { 38 | Log.d(TAG, "onOpen: received connection from client!"); 39 | } 40 | 41 | @Override 42 | public void onClose(WebSocket conn, int code, String reason, boolean remote) { 43 | Log.d(TAG, "onClose: client disconnected!"); 44 | } 45 | 46 | @Override 47 | public void onMessage( WebSocket conn, String message ) { 48 | Log.d(TAG, "onMessage: received message from client: "+message); 49 | try { 50 | final Packet packet = Packet.fromJson(new JSONObject(message)); 51 | switch (packet.getType()) { 52 | case FetchModsPacket.TYPE: 53 | handleFetchMod(conn, (FetchModsPacket) packet); 54 | break; 55 | case FetchRuntimePacket.TYPE: 56 | handleFetchRuntime(conn, (FetchRuntimePacket) packet); 57 | break; 58 | default: 59 | throw new IllegalArgumentException("invalid type:"+ packet.getType()); 60 | } 61 | } catch (IllegalArgumentException | JSONException e) { 62 | Log.w(TAG, "onMessage: received invalid message from client: "+message, e); 63 | conn.close(); 64 | } 65 | } 66 | 67 | private void handleFetchRuntime(WebSocket conn, FetchRuntimePacket packet) { 68 | final ProtocolRequestHandler.Runtime rt = handler.getRuntime(); 69 | final FetchRuntimeResponsePacket response = new FetchRuntimeResponsePacket(); 70 | 71 | try { 72 | for (Map.Entry entry : rt.nativeLibs.entrySet()) { 73 | response.nativeLibraries.put(entry.getKey(), Files.toByteArray(entry.getValue())); 74 | } 75 | for (Map.Entry entry : rt.jars.entrySet()) { 76 | response.runtimeJars.put(entry.getKey(), Files.toByteArray(entry.getValue())); 77 | } 78 | } catch (IOException e) { 79 | e.printStackTrace(); 80 | } 81 | 82 | conn.send(response.toJson().toString()); 83 | } 84 | 85 | private void handleFetchMod(WebSocket conn, FetchModsPacket packet) { 86 | final Map mods = handler.getMods(); 87 | final FetchModsResponsePacket response = new FetchModsResponsePacket(); 88 | 89 | try { 90 | for (Map.Entry entry : mods.entrySet()) { 91 | response.mods.put(entry.getKey(), Files.toByteArray(entry.getValue())); 92 | } 93 | } catch (IOException e) { 94 | throw new RuntimeException(e); 95 | } 96 | 97 | conn.send(response.toJson().toString()); 98 | } 99 | 100 | @Override 101 | public void onMessage( WebSocket conn, ByteBuffer message ) { 102 | Log.w(TAG, "onMessage: received invalid message from client: "+message); 103 | conn.close(); 104 | } 105 | 106 | @Override 107 | public void onError( WebSocket conn, Exception ex ) { 108 | Log.w(TAG, "onError: error with connection!", ex); 109 | if( conn != null ) { 110 | // some errors like port binding failed may not be assignable to a specific websocket 111 | } 112 | } 113 | 114 | @Override 115 | public void onStart() { 116 | Log.d(TAG, "server is now up and running!"); 117 | setConnectionLostTimeout(0); 118 | setConnectionLostTimeout(100); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/server/NeutronProtocolServer.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.server; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | 6 | import java.io.File; 7 | import java.io.IOException; 8 | import java.util.HashMap; 9 | import java.util.Map; 10 | 11 | public class NeutronProtocolServer { 12 | 13 | private static final int PORT = 32770; 14 | 15 | private LoadingWebsocketProtocolServer server; 16 | 17 | public NeutronProtocolServer(Context context) { 18 | String modsDirPath = context.getExternalFilesDir(null).toString()+File.separator+"mods"; 19 | 20 | String runtimeDirPath = context.getExternalFilesDir(null).toString()+File.separator+"runtime"; 21 | String nativeLibPath = runtimeDirPath + File.separator + "libs"; 22 | String jarsPath = runtimeDirPath + File.separator + "jars"; 23 | 24 | server = new LoadingWebsocketProtocolServer(PORT, new ProtocolRequestHandler() { 25 | @Override 26 | public Runtime getRuntime() { 27 | Runtime rt = new Runtime(); 28 | Log.d("Neutron-ModServer", "reading runtime jar files from "+jarsPath); 29 | Log.d("Neutron-ModServer", "reading runtime native libraries from "+nativeLibPath); 30 | rt.nativeLibs = readAllFilesInDirectory(new File(nativeLibPath)); 31 | rt.jars = readAllFilesInDirectory(new File(jarsPath)); 32 | Log.d("Neutron-ModServer", "read "+ rt.nativeLibs.size()+" native libraries, "+ rt.jars.size()+" jars"); 33 | return rt; 34 | } 35 | 36 | @Override 37 | public Map getMods() { 38 | Log.d("Neutron-ModServer", "fetching mods from "+ modsDirPath); 39 | Map mods = readAllFilesInDirectory(new File(modsDirPath)); 40 | Log.d("Neutron-ModServer", "read "+ mods.size()+" mods"); 41 | return mods; 42 | } 43 | }); 44 | } 45 | 46 | public void start() { 47 | Log.d("Neutron-ModServer", "starting server"); 48 | server.start(); 49 | } 50 | 51 | public void stop() { 52 | try { 53 | server.stop(); 54 | } catch (IOException | InterruptedException e) { 55 | throw new RuntimeException(e); 56 | } 57 | } 58 | 59 | private static Map readAllFilesInDirectory(File dir) { 60 | Map files = new HashMap<>(); 61 | final File[] listFiles = dir.listFiles(); 62 | if(listFiles == null) { 63 | return files; 64 | } 65 | for (File file : listFiles) { 66 | if(!file.isDirectory()) { 67 | files.put(file.getName(), file); 68 | } 69 | } 70 | return files; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /app/src/main/java/com/mjtg/neutron/loader/protocol/server/ProtocolRequestHandler.java: -------------------------------------------------------------------------------- 1 | package com.mjtg.neutron.loader.protocol.server; 2 | 3 | import java.io.File; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | public interface ProtocolRequestHandler { 8 | 9 | public static class Runtime { 10 | 11 | public Map jars = new HashMap<>(); 12 | 13 | public Map nativeLibs = new HashMap<>(); 14 | 15 | } 16 | 17 | Runtime getRuntime(); 18 | 19 | Map getMods(); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 13 | 19 | 22 | 25 | 26 | 27 | 28 | 34 | 35 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/file.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeutronLauncher-Dev/Neutron/49b205768f370de26a113cc430f5e5cc8c643e0a/app/src/main/res/drawable/file.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/folder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeutronLauncher-Dev/Neutron/49b205768f370de26a113cc430f5e5cc8c643e0a/app/src/main/res/drawable/folder.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 10 | 15 | 20 | 25 | 30 | 35 | 40 | 45 | 50 | 55 | 60 | 65 | 70 | 75 | 80 | 85 | 90 | 95 | 100 | 105 | 110 | 115 | 120 | 125 | 130 | 135 | 140 | 145 | 150 | 155 | 160 | 165 | 170 | 171 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/pack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NeutronLauncher-Dev/Neutron/49b205768f370de26a113cc430f5e5cc8c643e0a/app/src/main/res/drawable/pack.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | 32 | 33 | 34 | 35 | 46 | 47 | 53 | 54 | 55 | 66 | 67 | 77 | 78 | 79 | 90 | 91 | 101 | 102 | 103 | 114 | 115 | 125 | 126 | 127 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_file.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 22 | 23 | 27 | 28 | 33 | 34 | 44 | 45 | 46 | 47 | 55 | 56 | 57 | 58 | 59 | 60 | 70 | 71 | 79 | 80 | 81 | 89 | 90 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 16 | 17 | 20 | 21 | 28 | 29 |