├── .gitignore
├── .gitmodules
├── .idea
├── .name
├── codeStyles
│ └── Project.xml
├── gradle.xml
├── misc.xml
├── runConfigurations.xml
└── vcs.xml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── io
│ │ └── mrarm
│ │ └── mcversion
│ │ └── ExampleInstrumentedTest.java
│ ├── main
│ ├── AndroidManifest.xml
│ ├── cpp
│ │ ├── CMakeLists.txt
│ │ └── native-lib.cpp
│ ├── ic_launcher-web.png
│ ├── java
│ │ └── io
│ │ │ └── mrarm
│ │ │ └── mcversion
│ │ │ ├── FileDownloader.java
│ │ │ ├── GoogleLoginActivity.java
│ │ │ ├── IOUtil.java
│ │ │ ├── MainActivity.java
│ │ │ ├── PlayApi.java
│ │ │ ├── PlayHelper.java
│ │ │ ├── UiThreadHelper.java
│ │ │ ├── UiVersion.java
│ │ │ ├── VersionDownloader.java
│ │ │ ├── VersionInstaller.java
│ │ │ ├── VersionList.java
│ │ │ └── VersionListAdapter.java
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_launcher_background.xml
│ │ └── ic_launcher_foreground.xml
│ │ ├── layout
│ │ ├── activity_main.xml
│ │ └── version_entry.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
│ │ ├── raw
│ │ ├── device_arm64.conf
│ │ └── google_ca.pem
│ │ └── values
│ │ ├── colors.xml
│ │ ├── dimens.xml
│ │ ├── ic_launcher_background.xml
│ │ ├── strings.xml
│ │ └── styles.xml
│ └── test
│ └── java
│ └── io
│ └── mrarm
│ └── mcversion
│ └── ExampleUnitTest.java
├── build.gradle
├── deps
├── boringssl.cmake
├── curl.cmake
├── libuv.cmake
├── modules
│ ├── FindCURL.cmake
│ ├── FindOpenSSL.cmake
│ └── FindProtobuf.cmake
└── protobuf.cmake
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | /deps/protoc
16 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "deps/google-play-api"]
2 | path = deps/google-play-api
3 | url = git@github.com:MCMrARM/Google-Play-API.git
4 | [submodule "deps/protobuf"]
5 | path = deps/protobuf
6 | url = git@github.com:protocolbuffers/protobuf.git
7 |
--------------------------------------------------------------------------------
/.idea/.name:
--------------------------------------------------------------------------------
1 | MinecraftVersion
--------------------------------------------------------------------------------
/.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 | ANDROID_ATTRIBUTE_ORDER
95 |
96 |
97 |
98 |
99 |
100 |
101 | .*
102 |
103 | .*
104 |
105 |
106 | BY_NAME
107 |
108 |
109 |
110 |
111 |
112 |
113 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
15 |
16 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | buildToolsVersion "29.0.3"
6 | defaultConfig {
7 | applicationId "io.mrarm.mcversion"
8 | minSdkVersion 17
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0"
12 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
13 | externalNativeBuild {
14 | cmake {
15 | cppFlags ""
16 | }
17 | }
18 | ndk {
19 | abiFilters "arm64-v8a"
20 | }
21 | }
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | dataBinding {
29 | enabled = true
30 | }
31 | externalNativeBuild {
32 | cmake {
33 | path "src/main/cpp/CMakeLists.txt"
34 | }
35 | }
36 | compileOptions {
37 | sourceCompatibility = 1.8
38 | targetCompatibility = 1.8
39 | }
40 | }
41 |
42 | dependencies {
43 | implementation fileTree(dir: 'libs', include: ['*.jar'])
44 | implementation 'androidx.appcompat:appcompat:1.1.0'
45 | implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
46 | implementation 'com.google.android.material:material:1.1.0'
47 | implementation 'com.google.code.gson:gson:2.8.6'
48 | implementation('com.github.mcmrarm:dataadapter:master-SNAPSHOT') {changing = true}
49 | implementation('com.github.mcmrarm:observabletransform:master-SNAPSHOT') {changing = true}
50 | testImplementation 'junit:junit:4.12'
51 | androidTestImplementation 'androidx.test.ext:junit:1.1.0'
52 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
53 | }
54 |
--------------------------------------------------------------------------------
/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/io/mrarm/mcversion/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.test.platform.app.InstrumentationRegistry;
6 | import androidx.test.ext.junit.runners.AndroidJUnit4;
7 |
8 | import org.junit.Test;
9 | import org.junit.runner.RunWith;
10 |
11 | import static org.junit.Assert.*;
12 |
13 | /**
14 | * Instrumented test, which will execute on an Android device.
15 | *
16 | * @see Testing documentation
17 | */
18 | @RunWith(AndroidJUnit4.class)
19 | public class ExampleInstrumentedTest {
20 | @Test
21 | public void useAppContext() {
22 | // Context of the app under test.
23 | Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
24 |
25 | assertEquals("io.mrarm.mcversion", appContext.getPackageName());
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
17 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/app/src/main/cpp/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | cmake_minimum_required(VERSION 3.4.1)
2 |
3 | set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/../../../../deps/modules" ${CMAKE_MODULE_PATH})
4 |
5 | set(CROSS_COMPILE_ARSG "-G${CMAKE_GENERATOR}")
6 | set(CROSS_COMPILE_CACHE_ARGS "-DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE}"
7 | "-DANDROID_ABI:STRING=${ANDROID_ABI}" "-DANDROID_PLATFORM:STRING=${ANDROID_PLATFORM}"
8 | "-DANDROID_NDK:PATH=${ANDROID_NDK}" "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}")
9 |
10 | include(../../../../deps/boringssl.cmake)
11 | include(../../../../deps/curl.cmake)
12 | include(../../../../deps/libuv.cmake)
13 | include(../../../../deps/protobuf.cmake)
14 | add_subdirectory(../../../../deps/google-play-api google-play-api)
15 |
16 | add_library(native-lib SHARED native-lib.cpp ../../../../deps/google-play-api/src/config.cpp)
17 |
18 | target_link_libraries(native-lib gplayapi)
--------------------------------------------------------------------------------
/app/src/main/cpp/native-lib.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 | #include "../../../../deps/google-play-api/src/config.h"
11 |
12 | JavaVM *jvm;
13 |
14 | class JVMAttacher {
15 | private:
16 | JNIEnv *envPtr;
17 | bool needsDetach = false;
18 |
19 | public:
20 | JVMAttacher() {
21 | int ret = jvm->GetEnv((void **) &envPtr, JNI_VERSION_1_4);
22 | if (ret == JNI_EDETACHED) {
23 | needsDetach = true;
24 | if (jvm->AttachCurrentThread(&envPtr, NULL) != 0)
25 | throw std::runtime_error("AttachCurrentThread failed");
26 | } else if (ret != JNI_OK) {
27 | throw std::runtime_error("GetEnv failed");
28 | }
29 | }
30 |
31 | ~JVMAttacher() {
32 | if (needsDetach) {
33 | jvm->DetachCurrentThread();
34 | }
35 | }
36 |
37 | JNIEnv *env() const {
38 | return envPtr;
39 | }
40 | };
41 |
42 | extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {
43 | jvm = vm;
44 | return JNI_VERSION_1_4;
45 | }
46 |
47 |
48 | struct NativePlayApi {
49 | playapi::device_info device;
50 | playapi::file_login_cache loginCache;
51 | playapi::login_api loginApi;
52 | playapi::api api;
53 | std::unique_ptr deviceConfig;
54 |
55 | NativePlayApi(std::string dataPath) : loginCache(dataPath + "login_cache.json"), loginApi(device, loginCache), api(device) {
56 | }
57 | };
58 |
59 | static std::string stringFromJava(JNIEnv *env, jstring str) {
60 | const char *cstr = env->GetStringUTFChars(str, nullptr);
61 | std::string ret (cstr);
62 | env->ReleaseStringUTFChars(str, cstr);
63 | return ret;
64 | }
65 |
66 |
67 | extern "C" JNIEXPORT void JNICALL
68 | Java_io_mrarm_mcversion_PlayApi_initCurlSsl(JNIEnv* env, jclass, jstring jCaPath) {
69 | std::string caPath = stringFromJava(env, jCaPath);
70 | playapi::http_request::set_platform_curl_init_hook([caPath](CURL *curl) {
71 | curl_easy_setopt(curl, CURLOPT_CAINFO, caPath.c_str());
72 | });
73 | }
74 |
75 | extern "C" JNIEXPORT jlong JNICALL
76 | Java_io_mrarm_mcversion_PlayApi_init(JNIEnv *env, jclass, jstring jDataPath) {
77 | auto obj = new NativePlayApi(stringFromJava(env, jDataPath));
78 | return (jlong) (uintptr_t) obj;
79 | }
80 |
81 | extern "C" JNIEXPORT void JNICALL
82 | Java_io_mrarm_mcversion_PlayApi_setDevice(JNIEnv *env, jclass, jlong jSelf, jstring jDeviceConfig,
83 | jstring jStatePath) {
84 | auto self = (NativePlayApi *) (size_t) jSelf;
85 |
86 | {
87 | std::stringstream deviceConfig(stringFromJava(env, jDeviceConfig));
88 | playapi::config devInfoConfig;
89 | devInfoConfig.load(deviceConfig);
90 | self->device.load(devInfoConfig);
91 | }
92 |
93 | self->deviceConfig = std::make_unique(stringFromJava(env, jStatePath));
94 | self->deviceConfig->load();
95 | self->deviceConfig->load_device_info_data(self->device);
96 | self->device.generate_fields();
97 | self->deviceConfig->set_device_info_data(self->device);
98 | self->deviceConfig->save();
99 | self->loginApi.set_checkin_data(self->deviceConfig->checkin_data);
100 | }
101 |
102 | extern "C" JNIEXPORT void JNICALL
103 | Java_io_mrarm_mcversion_PlayApi_setLoginToken(JNIEnv *env, jclass, jlong jSelf,
104 | jstring jId, jstring jToken) {
105 | auto self = (NativePlayApi *) (size_t) jSelf;
106 |
107 | self->loginApi.set_token(stringFromJava(env, jId), stringFromJava(env, jToken));
108 | }
109 |
110 | static std::function makeErrorCallback(jobject gCallback) {
111 | return [gCallback](std::exception_ptr err) {
112 | try {
113 | std::rethrow_exception(err);
114 | } catch (std::exception &e) {
115 | JVMAttacher attacher;
116 | auto mid = attacher.env()->GetMethodID(attacher.env()->GetObjectClass(gCallback), "onError", "(Ljava/lang/String;)V");
117 | attacher.env()->CallVoidMethod(gCallback, mid, attacher.env()->NewStringUTF(e.what()));
118 | attacher.env()->DeleteGlobalRef(gCallback);
119 | }
120 | };
121 | }
122 |
123 | extern "C" JNIEXPORT void JNICALL
124 | Java_io_mrarm_mcversion_PlayApi_loginWithAccessToken(JNIEnv *env, jclass, jlong jSelf,
125 | jstring jId, jstring jToken, jobject callback) {
126 | auto self = (NativePlayApi *) (size_t) jSelf;
127 | auto gCallback = env->NewGlobalRef(callback);
128 |
129 | self->loginApi.perform_with_access_token(stringFromJava(env, jToken), stringFromJava(env, jId), true)->call([self, gCallback]() {
130 | JVMAttacher attacher;
131 | auto mid = attacher.env()->GetMethodID(attacher.env()->GetObjectClass(gCallback), "onSuccess", "(Ljava/lang/String;)V");
132 | attacher.env()->CallVoidMethod(gCallback, mid, attacher.env()->NewStringUTF(self->loginApi.get_token().c_str()));
133 | attacher.env()->DeleteGlobalRef(gCallback);
134 | }, makeErrorCallback(gCallback));
135 | }
136 |
137 | static void doCheckIn(NativePlayApi *self, std::function successCb, std::function errCb) {
138 | if (self->deviceConfig->checkin_data.android_id == 0) {
139 | auto checkin = std::make_shared(self->device);
140 | checkin->add_auth(self->loginApi)->call([self, checkin, successCb, errCb]() {
141 | checkin->perform_checkin()->call([self, checkin, successCb](auto data) {
142 | self->deviceConfig->checkin_data = data;
143 | self->deviceConfig->save();
144 | successCb();
145 | }, errCb);
146 | }, errCb);
147 | } else {
148 | successCb();
149 | }
150 | }
151 |
152 | static void doAuthToApi(NativePlayApi *self, std::function successCb, std::function errCb) {
153 | self->api.set_checkin_data(self->deviceConfig->checkin_data);
154 | self->deviceConfig->load_api_data(self->loginApi.get_email(), self->api);
155 | self->api.set_auth(self->loginApi)->call([self, successCb, errCb]() {
156 | if (self->api.device_config_token.length() == 0) {
157 | self->api.fetch_toc()->call([self, successCb, errCb](auto toc) {
158 | if (toc.payload().tocresponse().requiresuploaddeviceconfig()) {
159 | self->api.upload_device_config()->call([self, successCb](auto resp) {
160 | self->api.info_mutex.lock();
161 | self->api.device_config_token = resp.payload().uploaddeviceconfigresponse().uploaddeviceconfigtoken();
162 | self->deviceConfig->set_api_data(self->loginApi.get_email(), self->api);
163 | self->api.info_mutex.unlock();
164 |
165 | self->deviceConfig->save();
166 | successCb();
167 | }, errCb);
168 | } else {
169 | successCb();
170 | }
171 | }, errCb);
172 | } else {
173 | successCb();
174 | }
175 | }, errCb);
176 | }
177 |
178 | extern "C" JNIEXPORT void JNICALL
179 | Java_io_mrarm_mcversion_PlayApi_authToApi(JNIEnv *env, jclass, jlong jSelf, jobject callback) {
180 | auto self = (NativePlayApi *) (size_t) jSelf;
181 | auto gCallback = env->NewGlobalRef(callback);
182 |
183 | auto errCb = makeErrorCallback(gCallback);
184 |
185 | doCheckIn(self, [self, errCb, gCallback]() {
186 | doAuthToApi(self, [gCallback]() {
187 | JVMAttacher attacher;
188 | auto mid = attacher.env()->GetMethodID(attacher.env()->GetObjectClass(gCallback), "onSuccess", "()V");
189 | attacher.env()->CallVoidMethod(gCallback, mid);
190 | attacher.env()->DeleteGlobalRef(gCallback);
191 | }, errCb);
192 | }, errCb);
193 | }
194 |
195 | extern "C" JNIEXPORT void JNICALL
196 | Java_io_mrarm_mcversion_PlayApi_requestDelivery(JNIEnv *env, jclass, jlong jSelf,
197 | jstring jPkg, jint version, jobject callback) {
198 | auto self = (NativePlayApi *) (size_t) jSelf;
199 | auto gCallback = env->NewGlobalRef(callback);
200 |
201 | self->api.delivery(stringFromJava(env, jPkg), version, std::string())->call([self, gCallback](auto resp) {
202 | JVMAttacher attacher;
203 | auto env = attacher.env();
204 |
205 | auto const &dd = resp.payload().deliveryresponse().appdeliverydata();
206 | std::vector downloadLinks;
207 | std::vector downloadSizes;
208 | if (dd.has_downloadurl() || dd.has_gzippeddownloadurl()) {
209 | downloadLinks.push_back("main");
210 | downloadLinks.push_back(dd.downloadurl());
211 | downloadLinks.push_back(dd.gzippeddownloadurl());
212 | downloadSizes.push_back(dd.downloadsize());
213 | }
214 | for (auto const &d : dd.splitdeliverydata()) {
215 | downloadLinks.push_back(d.id());
216 | downloadLinks.push_back(d.downloadurl());
217 | downloadLinks.push_back(d.gzippeddownloadurl());
218 | downloadSizes.push_back(d.downloadsize());
219 | }
220 |
221 | auto arr = env->NewObjectArray((jsize) downloadLinks.size(), env->FindClass("java/lang/String"), nullptr);
222 | for (ssize_t i = downloadLinks.size() - 1; i >= 0; --i) {
223 | auto ref = env->NewStringUTF(downloadLinks[i].c_str());
224 | env->SetObjectArrayElement(arr, (jsize) i, ref);
225 | env->DeleteLocalRef(ref);
226 | }
227 | auto arr2 = env->NewLongArray((jsize) downloadSizes.size());
228 | env->SetLongArrayRegion(arr2, 0, (jsize) downloadSizes.size(), (const jlong *) downloadSizes.data());
229 |
230 | auto mid = env->GetMethodID(env->GetObjectClass(gCallback), "onSuccess", "([Ljava/lang/String;[J)V");
231 | env->CallVoidMethod(gCallback, mid, arr, arr2);
232 | env->DeleteGlobalRef(gCallback);
233 | }, makeErrorCallback(gCallback));
234 | }
235 |
--------------------------------------------------------------------------------
/app/src/main/ic_launcher-web.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/ic_launcher-web.png
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/FileDownloader.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import java.io.File;
4 | import java.io.FileOutputStream;
5 | import java.io.IOException;
6 | import java.io.InputStream;
7 | import java.io.OutputStream;
8 | import java.net.HttpURLConnection;
9 | import java.net.URL;
10 |
11 | public class FileDownloader {
12 |
13 | private HttpURLConnection conn;
14 | private CompletionCallback completionCallback;
15 | private ErrorCallback errorCallback;
16 | private ProgressCallback progressCallback;
17 |
18 | private synchronized void assertNotExecuted() {
19 | if (conn != null)
20 | throw new RuntimeException("Can only set conn before execute()");
21 | }
22 |
23 | public void setCompletionCallback(CompletionCallback completionCallback) {
24 | assertNotExecuted();
25 | this.completionCallback = completionCallback;
26 | }
27 |
28 | public void setErrorCallback(ErrorCallback errorCallback) {
29 | assertNotExecuted();
30 | this.errorCallback = errorCallback;
31 | }
32 |
33 | public void setProgressCallback(ProgressCallback progressCallback) {
34 | assertNotExecuted();
35 | this.progressCallback = progressCallback;
36 | }
37 |
38 | public void execute(String url, File outPath) {
39 | try {
40 | HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
41 | synchronized (this) {
42 | this.conn = conn;
43 | }
44 | if (conn == null)
45 | throw new IOException();
46 | long contentLength = conn.getContentLength();
47 | try (InputStream inStream = conn.getInputStream();
48 | OutputStream outStream = new FileOutputStream(outPath)) {
49 | byte[] b = new byte[128 * 1024];
50 | int n;
51 | long downloaded = 0;
52 | while ((n = inStream.read(b)) > 0) {
53 | outStream.write(b, 0, n);
54 | downloaded += n;
55 | if (progressCallback != null)
56 | progressCallback.onProgress(downloaded, contentLength);
57 | }
58 | }
59 | completionCallback.onComplete();
60 | } catch (IOException e) {
61 | errorCallback.onError(e);
62 | } finally {
63 | if (conn != null)
64 | conn.disconnect();
65 | }
66 | }
67 |
68 | public synchronized void cancel() {
69 | if (conn != null)
70 | conn.disconnect();
71 | }
72 |
73 | public interface CompletionCallback {
74 | void onComplete();
75 | }
76 |
77 | public interface ErrorCallback {
78 | void onError(IOException exception);
79 | }
80 |
81 | public interface ProgressCallback {
82 | void onProgress(long downloaded, long total);
83 | }
84 |
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/GoogleLoginActivity.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.graphics.Bitmap;
4 | import android.os.Bundle;
5 | import android.util.Log;
6 | import android.webkit.CookieManager;
7 | import android.webkit.JavascriptInterface;
8 | import android.webkit.WebView;
9 | import android.webkit.WebViewClient;
10 | import android.widget.Toast;
11 |
12 | import androidx.appcompat.app.AppCompatActivity;
13 |
14 | public class GoogleLoginActivity extends AppCompatActivity {
15 |
16 | private String accountIdentifier;
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 |
22 | CookieManager.getInstance().removeAllCookie();
23 |
24 | WebView view = new WebView(this);
25 | view.addJavascriptInterface(new LoginInterface(), "mm");
26 | view.setWebViewClient(new WebViewClient() {
27 |
28 | @Override
29 | public void doUpdateVisitedHistory(WebView view, String url, boolean isReload) {
30 | if (view.getUrl().endsWith("#close")) {
31 | completeLogin();
32 | }
33 | super.doUpdateVisitedHistory(view, url, isReload);
34 | }
35 | });
36 | view.getSettings().setJavaScriptEnabled(true);
37 | setContentView(view);
38 | view.loadUrl("https://accounts.google.com/embedded/setup/v2/android?source=com.android.settings&xoauth_display_name=Android%20Phone&canFrp=1&canSk=1&lang=en&langCountry=en_us&hl=en-US&cc=us");
39 | }
40 |
41 | private void completeLogin() {
42 | String accountToken = null;
43 |
44 | String cookies = CookieManager.getInstance().getCookie("https://accounts.google.com/");
45 | Log.d("GoogleLoginActivity", "Cookies: " + cookies);
46 | for (String c : cookies.split(";")) {
47 | String v = c.trim();
48 | int iof = v.indexOf("=");
49 | if (iof == -1)
50 | continue;
51 | String k = v.substring(0, iof);
52 | v = v.substring(iof + 1);
53 | if (k.equals("oauth_token"))
54 | accountToken = v;
55 | }
56 | CookieManager.getInstance().removeAllCookie();
57 |
58 | if (accountToken == null)
59 | return;
60 |
61 | Log.d("GoogleLoginActivity", "Oauth token: " + accountToken);
62 |
63 | PlayHelper.getInstance(this).getApi().loginWithAccessToken(accountIdentifier, accountToken, new PlayApi.AccessTokenCallback() {
64 | @Override
65 | public void onSuccess(String token) {
66 | Log.d("GoogleLoginActivity", "Login success! " + token);
67 | PlayHelper.getInstance(GoogleLoginActivity.this).saveAccount(accountIdentifier, token);
68 | PlayHelper.getInstance(GoogleLoginActivity.this).requestAuthToApi();
69 |
70 | finish();
71 | }
72 |
73 | @Override
74 | public void onError(String str) {
75 | Log.e("GoogleLoginActivity", "Login failed! " + str);
76 | runOnUiThread(() -> Toast.makeText(GoogleLoginActivity.this, str, Toast.LENGTH_LONG).show());
77 | finish();
78 | }
79 | });
80 | }
81 |
82 | public class LoginInterface {
83 |
84 | @JavascriptInterface
85 | public void showView() {
86 | }
87 |
88 | @JavascriptInterface
89 | public void setAccountIdentifier(String identifier) {
90 | Log.d("GoogleLoginActivity", "Account id: " + identifier);
91 | accountIdentifier = identifier;
92 | }
93 |
94 | @JavascriptInterface
95 | public void log(String text) {
96 | Log.d("GoogleLoginActivity", "WWW: " + text);
97 | }
98 |
99 | }
100 |
101 | }
102 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/IOUtil.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.content.Context;
4 |
5 | import java.io.File;
6 | import java.io.FileOutputStream;
7 | import java.io.IOException;
8 | import java.io.InputStream;
9 | import java.io.OutputStream;
10 |
11 | public final class IOUtil {
12 |
13 | private IOUtil() {
14 | }
15 |
16 | public static byte[] readRawResource(Context ctx, int resId) {
17 | try (InputStream stream = ctx.getResources().openRawResource(resId)) {
18 | byte[] b = new byte[stream.available()];
19 | int o = 0;
20 | while (o < b.length) {
21 | int r = stream.read(b, o, b.length - o);
22 | if (r == -1)
23 | throw new IOException();
24 | o += r;
25 | }
26 | return b;
27 | } catch (IOException e) {
28 | throw new RuntimeException(e);
29 | }
30 | }
31 |
32 | public static void copyRawResource(Context ctx, int resId, File outPath) {
33 | try (InputStream inStream = ctx.getResources().openRawResource(resId);
34 | OutputStream outStream = new FileOutputStream(outPath)) {
35 | byte[] b = new byte[16 * 1024];
36 | int n;
37 | while ((n = inStream.read(b)) >= 0)
38 | outStream.write(b, 0, n);
39 | } catch (IOException e) {
40 | throw new RuntimeException(e);
41 | }
42 | }
43 |
44 | public static void deleteDirectory(File file) {
45 | File[] files = file.listFiles();
46 | if (files != null) {
47 | for (File child : files)
48 | deleteDirectory(child);
49 | }
50 | file.delete();
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/MainActivity.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import androidx.appcompat.app.AppCompatActivity;
4 | import androidx.databinding.DataBindingUtil;
5 | import androidx.recyclerview.widget.LinearLayoutManager;
6 |
7 | import android.content.Intent;
8 | import android.os.AsyncTask;
9 | import android.os.Bundle;
10 | import android.util.Log;
11 | import android.widget.Toast;
12 |
13 | import java.io.File;
14 | import java.io.IOException;
15 | import java.util.ArrayList;
16 |
17 | import io.mrarm.mcversion.databinding.ActivityMainBinding;
18 |
19 | public class MainActivity extends AppCompatActivity {
20 |
21 | static {
22 | System.loadLibrary("native-lib");
23 | }
24 |
25 | private VersionInstaller installer;
26 |
27 | @Override
28 | protected void onCreate(Bundle savedInstanceState) {
29 | super.onCreate(savedInstanceState);
30 |
31 | ActivityMainBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_main);
32 |
33 | VersionDownloader downloader = new VersionDownloader(new File(getFilesDir(), "minecraft"));
34 | installer = new VersionInstaller();
35 | installer.updateInstalledVersion(this);
36 | installer.deleteHangingSession(this);
37 | installer.registerIntentHandler(this);
38 | VersionListAdapter adapter = new VersionListAdapter(downloader, installer);
39 |
40 | VersionList versionList = new VersionList();
41 | File versionListCacheFile = new File(getCacheDir(), "versions.json");
42 | versionList.loadCached(versionListCacheFile);
43 | AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> {
44 | try {
45 | Log.d("MainActivity", "Loading version list from network");
46 | versionList.loadNetwork("https://raw.githubusercontent.com/minecraft-linux/mcpelauncher-versiondb/master/versions.json");
47 | versionList.saveToCache(versionListCacheFile);
48 | Log.d("MainActivity", "Loaded version list from network: " + versionList.versions.size() + " versions");
49 | runOnUiThread(() -> adapter.setVersions(versionList.versions));
50 | } catch (IOException e) {
51 | Log.e("MainActivity", "Loading version list from network failed");
52 | runOnUiThread(() -> Toast.makeText(this, e.getLocalizedMessage(), Toast.LENGTH_SHORT).show());
53 | e.printStackTrace();
54 | }
55 | });
56 |
57 | adapter.setVersions(versionList.versions != null ? versionList.versions : new ArrayList<>());
58 |
59 | binding.content.setLayoutManager(new LinearLayoutManager(this));
60 | binding.content.setAdapter(adapter);
61 | setContentView(binding.content);
62 |
63 | if (!PlayHelper.getInstance(this).hasSavedAccount())
64 | openGoogleLogin();
65 | }
66 |
67 | @Override
68 | protected void onDestroy() {
69 | super.onDestroy();
70 | installer.unregisterIntentHandler(this);
71 | }
72 |
73 | private void openGoogleLogin() {
74 | Intent intent = new Intent(this, GoogleLoginActivity.class);
75 | startActivity(intent);
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/PlayApi.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | public final class PlayApi {
4 |
5 | private long handle;
6 |
7 | public PlayApi(String dataPath) {
8 | handle = init(dataPath);
9 | }
10 |
11 | public void setDevice(String deviceConfig, String statePath) {
12 | setDevice(handle, deviceConfig, statePath);
13 | }
14 |
15 | public void setLoginToken(String id, String token) {
16 | setLoginToken(handle, id, token);
17 | }
18 |
19 | public void loginWithAccessToken(String id, String token, AccessTokenCallback cb) {
20 | loginWithAccessToken(handle, id, token, cb);
21 | }
22 |
23 | public void authToApi(Callback callback) {
24 | authToApi(handle, callback);
25 | }
26 |
27 | public void requestDelivery(String pkgName, int pkgVer, DeliveryCallback cb) {
28 | requestDelivery(handle, pkgName, pkgVer, new NativeDeliveryCallback() {
29 | @Override
30 | public void onSuccess(String[] strings, long[] ints) {
31 | DownloadInfo[] info = new DownloadInfo[strings.length / 3];
32 | for (int i = strings.length / 3 - 1; i >= 0; --i)
33 | info[i] = new DownloadInfo(strings[3 * i], strings[3 * i + 1], strings[3 * i + 2], ints[i]);
34 | cb.onSuccess(info);
35 | }
36 |
37 | @Override
38 | public void onError(String str) {
39 | cb.onError(str);
40 | }
41 | });
42 | }
43 |
44 | public static native void initCurlSsl(String caPath);
45 |
46 | private static native long init(String dataPath);
47 |
48 | private static native long setDevice(long self, String deviceConfig, String statePath);
49 |
50 | private static native long setLoginToken(long self, String id, String token);
51 |
52 | private static native long loginWithAccessToken(long self, String id, String token, AccessTokenCallback callback);
53 |
54 | private static native long authToApi(long self, Callback callback);
55 |
56 | private static native long requestDelivery(long self, String pkgName, int pkgVer, NativeDeliveryCallback callback);
57 |
58 | public interface Callback {
59 |
60 | void onSuccess();
61 |
62 | void onError(String str);
63 |
64 | }
65 |
66 | public interface AccessTokenCallback {
67 |
68 | void onSuccess(String token);
69 |
70 | void onError(String str);
71 |
72 | }
73 |
74 | private interface NativeDeliveryCallback {
75 |
76 | void onSuccess(String[] strings, long[] ints);
77 |
78 | void onError(String str);
79 |
80 | }
81 |
82 | public interface DeliveryCallback {
83 |
84 | void onSuccess(DownloadInfo[] download);
85 |
86 | void onError(String str);
87 |
88 | }
89 |
90 | public static class DownloadInfo {
91 |
92 | private final String name, url, gzippedUrl;
93 | private final long dlSize;
94 |
95 | private DownloadInfo(String name, String url, String gzippedUrl, long dlSize) {
96 | this.name = name;
97 | this.url = url;
98 | this.gzippedUrl = gzippedUrl;
99 | this.dlSize = dlSize;
100 | }
101 |
102 | public String getName() {
103 | return name;
104 | }
105 |
106 | public String getUrl() {
107 | return url;
108 | }
109 |
110 | public String getGzippedUrl() {
111 | return gzippedUrl;
112 | }
113 |
114 | public long getSize() {
115 | return dlSize;
116 | }
117 | }
118 |
119 | }
120 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/PlayHelper.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.preference.PreferenceManager;
6 | import android.util.Log;
7 | import android.widget.Toast;
8 |
9 | import java.io.File;
10 | import java.io.UnsupportedEncodingException;
11 |
12 | public class PlayHelper {
13 |
14 | private static final String PREF_ACCOUNT_ID = "play_account_id";
15 | private static final String PREF_ACCOUNT_TOKEN = "play_account_token";
16 |
17 | private static PlayHelper instance;
18 |
19 | public static PlayHelper getInstance(Context context) {
20 | if (instance == null)
21 | instance = new PlayHelper(context.getApplicationContext());
22 | return instance;
23 | }
24 |
25 | private final Context context;
26 | private File dataDir;
27 | private PlayApi api;
28 | private boolean authToApiPending = false;
29 | private boolean authedToApi = false;
30 |
31 | public PlayHelper(Context context) {
32 | this.context = context;
33 | dataDir = new File(context.getFilesDir(), "playapi");
34 | dataDir.mkdirs();
35 | String dataDirS = dataDir.getAbsolutePath();
36 | if (!dataDirS.endsWith("/"))
37 | dataDirS += "/";
38 | initCurlSsl(context);
39 | api = new PlayApi(dataDirS);
40 | loadDevice(R.raw.device_arm64, "arm64");
41 | if (hasSavedAccount()) {
42 | loadAccountFromPrefs();
43 | requestAuthToApi();
44 | }
45 | }
46 |
47 | private static void initCurlSsl(Context context) {
48 | File outCaFile = new File(context.getFilesDir(), "google_ca.pem");
49 | IOUtil.copyRawResource(context, R.raw.google_ca, outCaFile);
50 | PlayApi.initCurlSsl(outCaFile.getAbsolutePath());
51 | }
52 |
53 | private void loadDevice(int resId, String stateName) {
54 | try {
55 | String deviceConfig = new String(IOUtil.readRawResource(context, resId), "UTF-8");
56 | api.setDevice(deviceConfig, new File(dataDir, "device-" + stateName + "-state.conf").getAbsolutePath());
57 | } catch (UnsupportedEncodingException e) {
58 | throw new RuntimeException(e);
59 | }
60 | }
61 |
62 | private void loadAccountFromPrefs() {
63 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
64 | String accountId = prefs.getString(PREF_ACCOUNT_ID, null);
65 | String accountToken = prefs.getString(PREF_ACCOUNT_TOKEN, null);
66 | if (accountId != null && accountToken != null)
67 | api.setLoginToken(accountId, accountToken);
68 | }
69 |
70 | public boolean hasSavedAccount() {
71 | SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
72 | return prefs.contains(PREF_ACCOUNT_ID) && prefs.contains(PREF_ACCOUNT_TOKEN);
73 | }
74 |
75 | public void saveAccount(String id, String token) {
76 | SharedPreferences.Editor prefs = PreferenceManager.getDefaultSharedPreferences(context).edit();
77 | prefs.putString(PREF_ACCOUNT_ID, id);
78 | prefs.putString(PREF_ACCOUNT_TOKEN, token);
79 | prefs.apply();
80 | }
81 |
82 | public PlayApi getApi() {
83 | return api;
84 | }
85 |
86 | public void requestAuthToApi() {
87 | synchronized (this) {
88 | if (authToApiPending)
89 | return;
90 | authToApiPending = true;
91 | }
92 | api.authToApi(new PlayApi.Callback() {
93 | @Override
94 | public void onSuccess() {
95 | Log.i("PlayHelper", "Authenticated to api");
96 | synchronized (PlayHelper.this) {
97 | authToApiPending = false;
98 | authedToApi = true;
99 | }
100 | }
101 |
102 | @Override
103 | public void onError(String str) {
104 | synchronized (PlayHelper.this) {
105 | authToApiPending = false;
106 | }
107 | UiThreadHelper.runOnUiThread(() -> Toast.makeText(context, str, Toast.LENGTH_LONG).show());
108 | }
109 | });
110 | }
111 |
112 | public synchronized boolean isAuthedToApi() {
113 | return authedToApi;
114 | }
115 |
116 | }
117 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/UiThreadHelper.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.os.Handler;
4 | import android.os.Looper;
5 |
6 | public class UiThreadHelper {
7 |
8 | private static final Handler sUiHandler = new Handler(Looper.getMainLooper());
9 |
10 | public static void runOnUiThread(Runnable r) {
11 | if (Looper.getMainLooper().getThread() == Thread.currentThread())
12 | r.run();
13 | else
14 | sUiHandler.post(r);
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/UiVersion.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import androidx.databinding.ObservableBoolean;
4 | import androidx.databinding.ObservableLong;
5 |
6 | public final class UiVersion {
7 |
8 | private final String name;
9 | private final int versionCode;
10 | private final ObservableBoolean downloaded = new ObservableBoolean();
11 | private final ObservableBoolean downloading = new ObservableBoolean();
12 | private final ObservableLong downloadTotal = new ObservableLong();
13 | private final ObservableLong downloadComplete = new ObservableLong();
14 |
15 | public UiVersion(String name, int versionCode) {
16 | this.name = name;
17 | this.versionCode = versionCode;
18 | }
19 |
20 | public String getName() {
21 | return name;
22 | }
23 |
24 | public int getVersionCode() {
25 | return versionCode;
26 | }
27 |
28 | public ObservableBoolean isDownloaded() {
29 | return downloaded;
30 | }
31 |
32 | public ObservableBoolean isDownloading() {
33 | return downloading;
34 | }
35 |
36 | public ObservableLong getDownloadTotal() {
37 | return downloadTotal;
38 | }
39 |
40 | public ObservableLong getDownloadComplete() {
41 | return downloadComplete;
42 | }
43 |
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/VersionDownloader.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.content.Context;
4 | import android.os.AsyncTask;
5 | import android.util.Log;
6 | import android.widget.Toast;
7 |
8 | import java.io.File;
9 |
10 | public class VersionDownloader {
11 |
12 | private File storageDir;
13 |
14 | public VersionDownloader(File storageDir) {
15 | this.storageDir = storageDir;
16 | }
17 |
18 | public File getVersionDir(UiVersion version) {
19 | return new File(storageDir, String.valueOf(version.getVersionCode()));
20 | }
21 |
22 | public void updateDownloadedStatus(UiVersion version) {
23 | version.isDownloaded().set(getVersionDir(version).exists());
24 | }
25 |
26 | public void download(Context context, UiVersion version) {
27 | if (!PlayHelper.getInstance(context).isAuthedToApi()) {
28 | Toast.makeText(context, R.string.error_api_not_authenticated, Toast.LENGTH_LONG).show();
29 | return;
30 | }
31 | if (version.isDownloading().get())
32 | return;
33 | version.getDownloadComplete().set(0);
34 | version.getDownloadTotal().set(0);
35 | version.isDownloading().set(true);
36 | PlayHelper.getInstance(context).getApi().requestDelivery("com.mojang.minecraftpe", version.getVersionCode(), new PlayApi.DeliveryCallback() {
37 | @Override
38 | public void onSuccess(PlayApi.DownloadInfo[] download) {
39 | for (PlayApi.DownloadInfo i : download)
40 | Log.d("VersionDownloader", "Download: " + i.getName() + " " + i.getGzippedUrl());
41 |
42 | UiThreadHelper.runOnUiThread(() -> {
43 | DownloadTracker tracker = new DownloadTracker(context, version, download, getVersionDir(version));
44 | tracker.downloadNextFile();
45 | });
46 | }
47 |
48 | @Override
49 | public void onError(String str) {
50 | UiThreadHelper.runOnUiThread(() -> {
51 | Toast.makeText(context, str, Toast.LENGTH_LONG).show();
52 | version.isDownloading().set(false);
53 | });
54 | }
55 | });
56 | }
57 |
58 | public void delete(UiVersion version) {
59 | IOUtil.deleteDirectory(getVersionDir(version));
60 | version.isDownloaded().set(false);
61 | }
62 |
63 | private static class DownloadTracker {
64 |
65 | private final Context context;
66 | private final UiVersion version;
67 | private final PlayApi.DownloadInfo[] downloads;
68 | private final File downloadDir;
69 | private long totalSize, totalDownloaded;
70 | private int nextFileIndex = 0;
71 |
72 | public DownloadTracker(Context context, UiVersion version, PlayApi.DownloadInfo[] downloads, File downloadDir) {
73 | this.context = context;
74 | this.version = version;
75 | this.downloads = downloads;
76 | this.downloadDir = downloadDir;
77 |
78 | for (PlayApi.DownloadInfo i : downloads)
79 | totalSize += i.getSize();
80 | version.getDownloadTotal().set(totalSize);
81 | }
82 |
83 | public void downloadNextFile() {
84 | if (nextFileIndex == downloads.length) {
85 | version.isDownloading().set(false);
86 | version.isDownloaded().set(true);
87 | return;
88 | }
89 |
90 | downloadDir.mkdirs();
91 |
92 | PlayApi.DownloadInfo dlInfo = downloads[nextFileIndex++];
93 | FileDownloader downloader = new FileDownloader();
94 | downloader.setProgressCallback((c, t) -> UiThreadHelper.runOnUiThread(() -> {
95 | version.getDownloadComplete().set(totalDownloaded + c);
96 | }));
97 | downloader.setCompletionCallback(() -> UiThreadHelper.runOnUiThread(() -> {
98 | totalDownloaded += dlInfo.getSize();
99 | version.getDownloadComplete().set(totalDownloaded);
100 | downloadNextFile();
101 | }));
102 | downloader.setErrorCallback(e -> UiThreadHelper.runOnUiThread(() -> {
103 | Toast.makeText(context, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
104 | e.printStackTrace();
105 |
106 | version.isDownloading().set(false);
107 | }));
108 | AsyncTask.THREAD_POOL_EXECUTOR.execute(() -> downloader.execute(dlInfo.getUrl(), new File(downloadDir, dlInfo.getName() + ".apk")));
109 | }
110 |
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/VersionInstaller.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.app.PendingIntent;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.content.pm.PackageInfo;
9 | import android.content.pm.PackageInstaller;
10 | import android.content.pm.PackageManager;
11 | import android.os.Build;
12 | import android.util.Log;
13 |
14 | import androidx.annotation.RequiresApi;
15 | import androidx.databinding.ObservableInt;
16 |
17 | import java.io.File;
18 | import java.io.FileInputStream;
19 | import java.io.IOException;
20 | import java.io.InputStream;
21 | import java.io.OutputStream;
22 | import java.util.HashMap;
23 | import java.util.Map;
24 | import java.util.UUID;
25 |
26 | public class VersionInstaller {
27 |
28 | private static final String ACTION_INSTALL_STATUS = "io.mrarm.mcversion.installstatus";
29 | private static final String ACTION_UNINSTALL_STATUS = "io.mrarm.mcversion.uninstallstatus";
30 | private static final String ARG_SESSION_ID = "session_id";
31 | private static final String ARG_CALLBACK_ID = "callback_id";
32 |
33 | private final ObservableInt installedVersion = new ObservableInt();
34 |
35 | private final Map callbackMap = new HashMap<>();
36 |
37 | private final BroadcastReceiver receiver = new BroadcastReceiver() {
38 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
39 | @Override
40 | public void onReceive(Context context, Intent intent) {
41 | final int statusCode = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
42 | if (statusCode == PackageInstaller.STATUS_PENDING_USER_ACTION) {
43 | Intent launchIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT);
44 | context.startActivity(launchIntent);
45 | return;
46 | }
47 | if (intent.getAction().equals(ACTION_INSTALL_STATUS)) {
48 | Log.d("VersionInstaller", "Install status: " + statusCode);
49 |
50 | PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
51 | try {
52 | packageInstaller.abandonSession(intent.getIntExtra(ARG_SESSION_ID, -1));
53 | } catch (SecurityException ignored) {
54 | }
55 | } else if (intent.getAction().equals(ACTION_UNINSTALL_STATUS)) {
56 | Log.d("VersionInstaller", "Uninstall status: " + statusCode);
57 | }
58 |
59 | updateInstalledVersion(context);
60 |
61 | if (intent.hasExtra(ARG_CALLBACK_ID)) {
62 | InstallCallback r = callbackMap.remove(intent.getStringExtra(ARG_CALLBACK_ID));
63 | if (r == null)
64 | return;
65 | r.onComplete(statusCode);
66 | }
67 | }
68 | };
69 |
70 | public void deleteHangingSession(Context context) {
71 | if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.LOLLIPOP) {
72 | PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
73 | for (PackageInstaller.SessionInfo s : packageInstaller.getMySessions()) {
74 | packageInstaller.abandonSession(s.getSessionId());
75 | }
76 | }
77 | }
78 |
79 | public void registerIntentHandler(Context context) {
80 | IntentFilter intentFilter = new IntentFilter();
81 | intentFilter.addAction(ACTION_INSTALL_STATUS);
82 | intentFilter.addAction(ACTION_UNINSTALL_STATUS);
83 | context.registerReceiver(receiver, intentFilter, null, null);
84 | }
85 |
86 | public void unregisterIntentHandler(Context context) {
87 | context.unregisterReceiver(receiver);
88 | }
89 |
90 | private String registerCallback(InstallCallback callback) {
91 | String uuid = UUID.randomUUID().toString();
92 | callbackMap.put(uuid, callback);
93 | return uuid;
94 | }
95 |
96 | public ObservableInt getInstalledVersion() {
97 | return installedVersion;
98 | }
99 |
100 | public void updateInstalledVersion(Context context) {
101 | try {
102 | PackageInfo info = context.getPackageManager().getPackageInfo("com.mojang.minecraftpe", 0);
103 | installedVersion.set(info.versionCode);
104 | } catch (PackageManager.NameNotFoundException e) {
105 | installedVersion.set(0);
106 | }
107 | }
108 |
109 | public boolean needsUninstall(Context context, int versionCode) {
110 | try {
111 | PackageInfo info = context.getPackageManager().getPackageInfo("com.mojang.minecraftpe", 0);
112 | return info.versionCode > versionCode;
113 | } catch (PackageManager.NameNotFoundException e) {
114 | return false;
115 | }
116 | }
117 |
118 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
119 | public void uninstall(Context context, InstallCallback callback) {
120 | PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
121 | Intent intent = new Intent(ACTION_UNINSTALL_STATUS);
122 | intent.putExtra(ARG_CALLBACK_ID, registerCallback(callback));
123 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 10000, intent, PendingIntent.FLAG_CANCEL_CURRENT);
124 | packageInstaller.uninstall("com.mojang.minecraftpe", pendingIntent.getIntentSender());
125 | }
126 |
127 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
128 | private void install(Context context, File versionDir, InstallCallback callback) throws IOException {
129 | PackageInstaller packageInstaller = context.getPackageManager().getPackageInstaller();
130 | PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
131 | PackageInstaller.SessionParams.MODE_FULL_INSTALL);
132 | params.setAppPackageName("com.mojang.minecraftpe");
133 | int sessionId = packageInstaller.createSession(params);
134 | PackageInstaller.Session session = packageInstaller.openSession(sessionId);
135 | for (File f : versionDir.listFiles()) {
136 | try (InputStream inStream = new FileInputStream(f);
137 | OutputStream outStream = session.openWrite(f.getName(), 0, -1)) {
138 | byte[] b = new byte[512 * 1024];
139 | int n;
140 | while ((n = inStream.read(b)) >= 0)
141 | outStream.write(b, 0, n);
142 | session.fsync(outStream);
143 | }
144 | }
145 |
146 | Intent intent = new Intent(ACTION_INSTALL_STATUS);
147 | intent.putExtra(ARG_SESSION_ID, sessionId);
148 | intent.putExtra(ARG_CALLBACK_ID, registerCallback(callback));
149 | PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 10001, intent, PendingIntent.FLAG_CANCEL_CURRENT);
150 | session.commit(pendingIntent.getIntentSender());
151 | session.close();
152 | }
153 |
154 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
155 | public void install(Context context, VersionDownloader downloader, UiVersion version, InstallCallback callback)
156 | throws IOException {
157 | install(context, downloader.getVersionDir(version), callback);
158 | }
159 |
160 | public interface InstallCallback {
161 | void onComplete(int status);
162 | }
163 |
164 | }
165 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/VersionList.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.annotations.SerializedName;
5 | import com.google.gson.reflect.TypeToken;
6 |
7 | import java.io.BufferedReader;
8 | import java.io.File;
9 | import java.io.FileReader;
10 | import java.io.FileWriter;
11 | import java.io.IOException;
12 | import java.io.InputStreamReader;
13 | import java.net.HttpURLConnection;
14 | import java.net.URL;
15 | import java.util.List;
16 | import java.util.Map;
17 |
18 | public class VersionList {
19 |
20 | private static Gson gson = new Gson();
21 |
22 | public List versions;
23 |
24 | public void loadCached(File file) {
25 | try (FileReader reader = new FileReader(file)) {
26 | versions = gson.fromJson(reader, new TypeToken>(){}.getType());
27 | } catch (IOException ignored) {
28 | }
29 | }
30 |
31 | public void saveToCache(File file) {
32 | try (FileWriter writer = new FileWriter(file)) {
33 | gson.toJson(versions, writer);
34 | } catch (IOException ignored) {
35 | }
36 | }
37 |
38 | public void loadNetwork(String url) throws IOException {
39 | HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
40 | if (conn == null)
41 | throw new IOException();
42 | try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()))) {
43 | List versions = gson.fromJson(reader, new TypeToken>(){}.getType());
44 | if (versions == null)
45 | throw new IOException("Version list empty");
46 | this.versions = versions;
47 | }
48 | }
49 |
50 |
51 | public static class Version {
52 | @SerializedName("version_name")
53 | public String name;
54 |
55 | @SerializedName("codes")
56 | public Map codes;
57 | }
58 |
59 |
60 | }
61 |
--------------------------------------------------------------------------------
/app/src/main/java/io/mrarm/mcversion/VersionListAdapter.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import android.app.AlertDialog;
4 | import android.content.Context;
5 | import android.content.pm.PackageInstaller;
6 | import android.os.Build;
7 | import android.widget.Toast;
8 |
9 | import androidx.databinding.ObservableInt;
10 |
11 | import java.io.IOException;
12 | import java.util.ArrayList;
13 | import java.util.Collections;
14 | import java.util.HashMap;
15 | import java.util.List;
16 | import java.util.Map;
17 |
18 | import io.mrarm.dataadapter.DataAdapter;
19 | import io.mrarm.dataadapter.ListData;
20 | import io.mrarm.dataadapter.ViewHolderType;
21 |
22 | public class VersionListAdapter extends DataAdapter {
23 |
24 | private static final String[] ABIS = {"arm64-v8a", "armeabi-v7a"};
25 |
26 | private final VersionDownloader downloader;
27 | private final VersionInstaller installer;
28 | private List currentVersions;
29 |
30 | public VersionListAdapter(VersionDownloader downloader, VersionInstaller installer) {
31 | this.downloader = downloader;
32 | this.installer = installer;
33 | }
34 |
35 | public ObservableInt getInstalledVersion() {
36 | return installer.getInstalledVersion();
37 | }
38 |
39 | public void setVersions(List versions) {
40 | Map oldDownloads = new HashMap<>();
41 | if (currentVersions != null) {
42 | for (UiVersion v : currentVersions)
43 | oldDownloads.put(v.getVersionCode(), v);
44 | }
45 |
46 | List uiVersions = new ArrayList<>();
47 | for (VersionList.Version v : versions) {
48 | for (String abi : ABIS) {
49 | Integer versionCode = v.codes.get(abi);
50 | if (versionCode == null)
51 | continue;
52 | if (oldDownloads.containsKey(versionCode))
53 | uiVersions.add(oldDownloads.get(versionCode));
54 | else
55 | uiVersions.add(new UiVersion(v.name + " (" + abi + ")", versionCode));
56 | }
57 | }
58 | for (UiVersion v : uiVersions)
59 | downloader.updateDownloadedStatus(v);
60 | Collections.reverse(uiVersions);
61 | ListData data = new ListData<>(uiVersions, ITEM);
62 | data.setContext(this);
63 | setSource(data);
64 | }
65 |
66 | private void doInstall(Context context, UiVersion version, Runnable cb) {
67 | try {
68 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
69 | installer.install(context, downloader, version, (s) -> cb.run());
70 | }
71 | } catch (IOException e) {
72 | Toast.makeText(context, e.getLocalizedMessage(), Toast.LENGTH_LONG).show();
73 | e.printStackTrace();
74 | }
75 | }
76 |
77 | public void downloadOrInstall(Context context, UiVersion version) {
78 | if (version.isDownloaded().get()) {
79 | AlertDialog dialog = new AlertDialog.Builder(context)
80 | .setMessage(R.string.text_reinstall)
81 | .setCancelable(false)
82 | .show();
83 |
84 | if (installer.needsUninstall(context, version.getVersionCode())) {
85 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
86 | installer.uninstall(context, (s) -> {
87 | if (s == PackageInstaller.STATUS_SUCCESS) {
88 | doInstall(context, version, dialog::dismiss);
89 | } else {
90 | dialog.dismiss();
91 | }
92 | });
93 | }
94 | } else {
95 | doInstall(context, version, dialog::dismiss);
96 | }
97 | } else {
98 | downloader.download(context, version);
99 | }
100 | }
101 |
102 | public boolean showLongPressMenu(Context context, UiVersion version) {
103 | if (!version.isDownloaded().get())
104 | return false;
105 | new AlertDialog.Builder(context)
106 | .setTitle(version.getName())
107 | .setItems(new CharSequence[] { context.getString(R.string.action_delete) }, (d, which) -> {
108 | if (which == 0) {
109 | downloader.delete(version);
110 | }
111 | })
112 | .show();
113 | return true;
114 | }
115 |
116 |
117 | private static final ViewHolderType ITEM =
118 | ViewHolderType.fromDataBinding(R.layout.version_entry)
119 | .setValueVarId(BR.version)
120 | .setContextVarId(BR.context)
121 | .build();
122 |
123 | }
124 |
--------------------------------------------------------------------------------
/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/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/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
9 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/version_entry.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
15 |
16 |
22 |
23 |
29 |
30 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/raw/device_arm64.conf:
--------------------------------------------------------------------------------
1 | ; data from my Zenphone 2 without a sim card running a quite old version of Cyanogenmod
2 | device_type = phone
3 | build.fingerprint = "asus/WW_Z00A/Z00A:5.0/LRX21V/2.20.40.165_20160118_6541_user:user/release-keys"
4 | build.id = "MOB3M"
5 | build.product = "mofd_v1"
6 | build.brand = "asus"
7 | build.radio = "1527_5.0.50.2_1225"
8 | build.bootloader = "unknown"
9 | build.client = "android-google"
10 | build.timestamp = 1466006494
11 | build.google_services = 10084470
12 | build.device = "Z00A"
13 | build.sdk_version = 23
14 | build.version_string = "6.0.1"
15 | build.model = "ASUS_Z00A"
16 | build.manufacturer = "asus"
17 | build.build_product = "WW_Z00A"
18 | build.ota_installed = true
19 | build.google_packages = [
20 | ms-android-google:2
21 | android-google:1
22 | gmm-android-google:4
23 | mvapp-android-google:5
24 | am-android-google:5
25 | ms-android-google:9
26 | ]
27 | build.security_patch = "2016-06-01"
28 |
29 | # undefinied, notouch, stylus, finger
30 | config.touch_screen = finger
31 | # undefinied, nokeys, qwerty, twelvekey
32 | config.keyboard = nokeys
33 | # undefinied, nonav, dpad, trackball, wheel
34 | config.navigation = nonav
35 | # undefinied, small, normal, large, xlarge
36 | config.screen_layout = normal
37 | config.has_hard_keyboard = false
38 | config.has_five_way_navig = false
39 | config.screen_density = 480
40 | config.gl_es_version = 0x30000
41 | config.system_shared_libraries = [
42 | android.test.runner,
43 | com.android.future.usb.accessory,
44 | com.android.location.provider,
45 | com.android.media.remotedisplay,
46 | com.android.mediadrm.signer,
47 | com.google.android.camera.experimental2015,
48 | com.google.android.dialer.support,
49 | com.google.android.gms,
50 | com.google.android.maps,
51 | com.google.android.media.effects,
52 | com.google.widevine.software.drm,
53 | javax.obex,
54 | org.apache.http.legacy,
55 | org.cyanogenmod.hardware,
56 | org.cyanogenmod.platform
57 | ]
58 | config.system_features = [
59 | ; some features may have a minimal GLES version specified. Specify it like this:
60 | ; some_gl_related_feature:min_gles_version
61 | android.hardware.audio.output,
62 | android.hardware.bluetooth,
63 | android.hardware.bluetooth_le,
64 | android.hardware.camera,
65 | android.hardware.camera.any,
66 | android.hardware.camera.autofocus,
67 | android.hardware.camera.flash,
68 | android.hardware.camera.front,
69 | android.hardware.ethernet,
70 | android.hardware.faketouch,
71 | android.hardware.location,
72 | android.hardware.location.gps,
73 | android.hardware.location.network,
74 | android.hardware.microphone,
75 | android.hardware.nfc,
76 | android.hardware.nfc.hce,
77 | android.hardware.screen.landscape,
78 | android.hardware.screen.portrait,
79 | android.hardware.sensor.accelerometer,
80 | android.hardware.sensor.compass,
81 | android.hardware.sensor.gyroscope,
82 | android.hardware.sensor.light,
83 | android.hardware.sensor.proximity,
84 | android.hardware.sensor.stepcounter,
85 | android.hardware.sensor.stepdetector,
86 | android.hardware.telephony,
87 | android.hardware.telephony.gsm,
88 | android.hardware.touchscreen,
89 | android.hardware.touchscreen.multitouch,
90 | android.hardware.touchscreen.multitouch.distinct,
91 | android.hardware.touchscreen.multitouch.jazzhand,
92 | android.hardware.usb.accessory,
93 | android.hardware.usb.host,
94 | android.hardware.wifi,
95 | android.hardware.wifi.direct,
96 | android.software.app_widgets,
97 | android.software.backup,
98 | android.software.connectionservice,
99 | android.software.device_admin,
100 | android.software.home_screen,
101 | android.software.input_methods,
102 | android.software.live_wallpaper,
103 | android.software.managed_users,
104 | android.software.print,
105 | android.software.sip,
106 | android.software.sip.voip,
107 | android.software.voice_recognizers,
108 | android.software.webview,
109 | asus.software.zenui,
110 | com.cyanogenmod.android,
111 | com.cyanogenmod.nfc.enhanced,
112 | com.google.android.feature.EXCHANGE_6_2,
113 | com.google.android.feature.GOOGLE_BUILD,
114 | com.google.android.feature.GOOGLE_EXPERIENCE,
115 | org.cyanogenmod.appsuggest,
116 | org.cyanogenmod.audio,
117 | org.cyanogenmod.hardware,
118 | org.cyanogenmod.livedisplay,
119 | org.cyanogenmod.livelockscreen,
120 | org.cyanogenmod.partner,
121 | org.cyanogenmod.performance,
122 | org.cyanogenmod.profiles,
123 | org.cyanogenmod.statusbar,
124 | org.cyanogenmod.telephony,
125 | org.cyanogenmod.theme,
126 | org.cyanogenmod.theme.v1,
127 | org.cyanogenmod.weather
128 | ]
129 | config.native_platforms = [
130 | arm64-v8a,
131 | armeabi-v7a,
132 | armeabi
133 | ]
134 | config.screen_width = 1080
135 | config.screen_height = 1920
136 | config.system_supported_locales = [
137 | af,
138 | af-ZA,
139 | am,
140 | am-ET,
141 | ar,
142 | ar-EG,
143 | ar-XB,
144 | ast-ES,
145 | az,
146 | az-AZ,
147 | be,
148 | bg,
149 | bg-BG,
150 | bn,
151 | bn-BD,
152 | bs,
153 | ca,
154 | ca-ES,
155 | cs,
156 | cs-CZ,
157 | da,
158 | da-DK,
159 | de,
160 | de-AT,
161 | de-CH,
162 | de-DE,
163 | de-LI,
164 | el,
165 | el-GR,
166 | en,
167 | en-AU,
168 | en-CA,
169 | en-GB,
170 | en-IN,
171 | en-NZ,
172 | en-SG,
173 | en-US,
174 | en-XA,
175 | eo,
176 | es,
177 | es-ES,
178 | es-US,
179 | et,
180 | et-EE,
181 | eu,
182 | eu-ES,
183 | fa,
184 | fa-IR,
185 | fi,
186 | fi-FI,
187 | fil,
188 | fil-PH,
189 | fr,
190 | fr-BE,
191 | fr-CA,
192 | fr-CH,
193 | fr-FR,
194 | gl,
195 | gl-ES,
196 | gu,
197 | gu-IN,
198 | hi,
199 | hi-IN,
200 | hr,
201 | hr-HR,
202 | hu,
203 | hu-HU,
204 | hy,
205 | hy-AM,
206 | in,
207 | in-ID,
208 | is,
209 | is-IS,
210 | it,
211 | it-CH,
212 | it-IT,
213 | iw,
214 | iw-IL,
215 | ja,
216 | ja-JP,
217 | ka,
218 | ka-GE,
219 | kk,
220 | kk-KZ,
221 | km,
222 | km-KH,
223 | kn,
224 | kn-IN,
225 | ko,
226 | ko-KR,
227 | ku,
228 | ku-IQ,
229 | ky,
230 | ky-KG,
231 | lb,
232 | lb-LU,
233 | lo,
234 | lo-LA,
235 | lt,
236 | lt-LT,
237 | lv,
238 | lv-LV,
239 | mk,
240 | mk-MK,
241 | ml,
242 | ml-IN,
243 | mn,
244 | mn-MN,
245 | mr,
246 | mr-IN,
247 | ms,
248 | ms-MY,
249 | my,
250 | my-MM,
251 | nb,
252 | nb-NO,
253 | ne,
254 | ne-NP,
255 | nl,
256 | nl-BE,
257 | nl-NL,
258 | pa,
259 | pa-IN,
260 | pl,
261 | pl-PL,
262 | pt,
263 | pt-BR,
264 | pt-PT,
265 | rm,
266 | rm-CH,
267 | ro,
268 | ro-RO,
269 | ru,
270 | ru-RU,
271 | si,
272 | si-LK,
273 | sk,
274 | sk-SK,
275 | sl,
276 | sl-SI,
277 | sq,
278 | sq-AL,
279 | sr,
280 | sr-Latn,
281 | sr-RS,
282 | sv,
283 | sv-SE,
284 | sw,
285 | sw-TZ,
286 | ta,
287 | ta-IN,
288 | te,
289 | te-IN,
290 | th,
291 | th-TH,
292 | tr,
293 | tr-TR,
294 | uk,
295 | uk-UA,
296 | ur,
297 | ur-PK,
298 | uz,
299 | uz-UZ,
300 | vi,
301 | vi-VN,
302 | zh,
303 | zh-CN,
304 | zh-HK,
305 | zh-TW,
306 | zu,
307 | zu-ZA
308 | ]
309 | config.gl_extensions = [
310 | GL_APPLE_texture_2D_limited_npot,
311 | GL_EXT_blend_minmax,
312 | GL_EXT_color_buffer_float,
313 | GL_EXT_debug_marker,
314 | GL_EXT_discard_framebuffer,
315 | GL_EXT_draw_buffers,
316 | GL_EXT_multi_draw_arrays,
317 | GL_EXT_multisampled_render_to_texture,
318 | GL_EXT_occlusion_query_boolean,
319 | GL_EXT_read_format_bgra,
320 | GL_EXT_robustness,
321 | GL_EXT_separate_shader_objects,
322 | GL_EXT_shader_framebuffer_fetch,
323 | GL_EXT_shader_texture_lod,
324 | GL_EXT_texture_filter_anisotropic,
325 | GL_EXT_texture_format_BGRA8888,
326 | GL_EXT_texture_rg,
327 | GL_EXT_texture_sRGB_decode,
328 | GL_IMG_multisampled_render_to_texture,
329 | GL_IMG_program_binary,
330 | GL_IMG_read_format,
331 | GL_IMG_shader_binary,
332 | GL_IMG_texture_compression_pvrtc,
333 | GL_IMG_texture_compression_pvrtc2,
334 | GL_IMG_texture_format_BGRA8888,
335 | GL_IMG_texture_npot,
336 | GL_IMG_vertex_array_object,
337 | GL_KHR_blend_equation_advanced,
338 | GL_KHR_blend_equation_advanced_coherent,
339 | GL_KHR_debug,
340 | GL_OES_EGL_image,
341 | GL_OES_EGL_image_external,
342 | GL_OES_EGL_sync,
343 | GL_OES_blend_equation_separate,
344 | GL_OES_blend_func_separate,
345 | GL_OES_blend_subtract,
346 | GL_OES_byte_coordinates,
347 | GL_OES_compressed_ETC1_RGB8_texture,
348 | GL_OES_compressed_paletted_texture,
349 | GL_OES_depth24,
350 | GL_OES_depth_texture,
351 | GL_OES_draw_texture,
352 | GL_OES_egl_sync,
353 | GL_OES_element_index_uint,
354 | GL_OES_extended_matrix_palette,
355 | GL_OES_fixed_point,
356 | GL_OES_fragment_precision_high,
357 | GL_OES_framebuffer_object,
358 | GL_OES_get_program_binary,
359 | GL_OES_mapbuffer,
360 | GL_OES_matrix_get,
361 | GL_OES_matrix_palette,
362 | GL_OES_packed_depth_stencil,
363 | GL_OES_point_size_array,
364 | GL_OES_point_sprite,
365 | GL_OES_query_matrix,
366 | GL_OES_read_format,
367 | GL_OES_required_internalformat,
368 | GL_OES_rgb8_rgba8,
369 | GL_OES_shader_image_atomic,
370 | GL_OES_single_precision,
371 | GL_OES_standard_derivatives,
372 | GL_OES_stencil8,
373 | GL_OES_stencil_wrap,
374 | GL_OES_surfaceless_context,
375 | GL_OES_texture_cube_map,
376 | GL_OES_texture_env_crossbar,
377 | GL_OES_texture_float,
378 | GL_OES_texture_half_float,
379 | GL_OES_texture_mirrored_repeat,
380 | GL_OES_texture_npot,
381 | GL_OES_texture_stencil8,
382 | GL_OES_texture_storage_multisample_2d_array,
383 | GL_OES_vertex_array_object,
384 | GL_OES_vertex_half_float
385 | ]
386 | config.smallest_screen_width_dp = 360
387 | config.low_ram = false
388 | config.total_mem = 4093796352
389 | config.cores = 4
390 | voice_capable = true
391 | wide_screen = false
392 | ota_certs = [
393 | "71Q6Rn2DDZl1zPDVaaeEHItd+Yg="
394 | ]
395 |
396 | ;
397 | ; OPTIONS THAT VARY BY DEVICE
398 | ; I have provided some example data here. You may customize them if you want.
399 | ;
400 |
401 | ; specify if the device is secured with a PIN, pattern of password
402 | config.keyguard_device_secure = true
403 |
404 | locale = "en_US"
405 | country = "us"
406 | time_zone = "America/New_York"
407 |
408 | roaming = "WIFI::"
409 | user_number = 0
410 | user_serial_number = 0
411 |
412 | mac_addr_type = wifi
413 | # let the program generate mac_addr&meid
414 | mac_addr = generate
415 | ; meid = generate - while this works it's rather better to omit this field than give a random value
416 | # generate a random 11 letter string as serial number
417 | serial_number = generate(12, "QWERTYUIOPASDFGHJKLZXCVBNM1234567890")
418 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 | 16dp
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #26A69A
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Minecraft Version
3 | Google Login
4 |
5 | Not downloaded
6 | Downloaded
7 | Installed
8 |
9 | Switching version…
10 |
11 | Delete
12 |
13 | Not authenticated to the API yet
14 |
15 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/app/src/test/java/io/mrarm/mcversion/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package io.mrarm.mcversion;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.5.3'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | mavenLocal()
20 | google()
21 | jcenter()
22 |
23 | }
24 | }
25 |
26 | task clean(type: Delete) {
27 | delete rootProject.buildDir
28 | }
29 |
--------------------------------------------------------------------------------
/deps/boringssl.cmake:
--------------------------------------------------------------------------------
1 | # Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | # ==============================================================================
15 | include (ExternalProject)
16 |
17 | set(boringssl_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/boringssl/src/boringssl/include)
18 | #set(boringssl_EXTRA_INCLUDE_DIR ${CMAKE_CURRENT_BINARY_DIR}/boringssl/src)
19 | set(boringssl_URL https://boringssl.googlesource.com/boringssl)
20 | set(boringssl_TAG eeb5bb3561347f121911781754cd464bf49e2c32)
21 | set(boringssl_BUILD ${CMAKE_BINARY_DIR}/boringssl/src/boringssl-build)
22 | #set(boringssl_LIBRARIES ${boringssl_BUILD}/obj/so/libboringssl.so)
23 | set(boringssl_STATIC_LIBRARIES
24 | ${boringssl_BUILD}/ssl/libssl.a
25 | ${boringssl_BUILD}/crypto/libcrypto.a
26 | ${boringssl_BUILD}/decrepit/libdecrepit.a
27 | )
28 | set(boringssl_EXTRA_OPTIONS )
29 | if ("${CMAKE_SYSTEM_PROCESSOR}" MATCHES arm*)
30 | set(boringssl_EXTRA_OPTIONS "-DANDROID_ARM_MODE:TEXT=arm")
31 | endif()
32 |
33 | ExternalProject_Add(boringssl_ext
34 | PREFIX boringssl
35 | SOURCE_DIR ${CMAKE_BINARY_DIR}/boringssl/src/boringssl
36 | BINARY_DIR ${boringssl_BUILD}
37 | GIT_REPOSITORY ${boringssl_URL}
38 | GIT_TAG ${boringssl_TAG}
39 | DOWNLOAD_DIR "${DOWNLOAD_LOCATION}"
40 | # BUILD_IN_SOURCE 1
41 | BUILD_BYPRODUCTS ${boringssl_STATIC_LIBRARIES}
42 | INSTALL_COMMAND ""
43 | CMAKE_ARGS ${CROSS_COMPILE_ARSG}
44 | CMAKE_CACHE_ARGS
45 | -DCMAKE_POSITION_INDEPENDENT_CODE:BOOL=${tensorflow_ENABLE_POSITION_INDEPENDENT_CODE}
46 | -DCMAKE_BUILD_TYPE:STRING=Release
47 | -DCMAKE_VERBOSE_MAKEFILE:BOOL=OFF
48 | ${CROSS_COMPILE_CACHE_ARGS}
49 | ${boringssl_EXTRA_OPTIONS}
50 | )
51 |
52 | file(MAKE_DIRECTORY ${boringssl_INCLUDE_DIR})
53 |
54 | add_library(boringssl INTERFACE IMPORTED)
55 | add_dependencies(boringssl boringssl_ext)
56 | set_property(TARGET boringssl PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${boringssl_INCLUDE_DIR})
57 | set_property(TARGET boringssl PROPERTY INTERFACE_LINK_LIBRARIES ${boringssl_STATIC_LIBRARIES})
58 |
59 | # define BoringSSL as OpenSSL for our purposes
60 |
61 | set(OPENSSL_INCLUDE_DIR ${boringssl_INCLUDE_DIR})
62 | set(OPENSSL_SSL_LIBRARY ${boringssl_BUILD}/ssl/libssl.a)
63 | set(OPENSSL_CRYPTO_LIBRARY ${boringssl_BUILD}/crypto/libcrypto.a)
64 |
--------------------------------------------------------------------------------
/deps/curl.cmake:
--------------------------------------------------------------------------------
1 | include(ExternalProject)
2 |
3 | set(CURL_OPTIONS "-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/ext/curl"
4 | "-DBUILD_CURL_EXE:BOOL=OFF"
5 | "-DBUILD_SHARED_LIBS:BOOL=OFF"
6 | "-DCURL_STATICLIB:BOOL=ON"
7 | "-DCURL_DISABLE_LDAP:BOOL=ON"
8 | "-DCMAKE_USE_LIBSSH2:BOOL=OFF"
9 | "-DCMAKE_USE_OPENLDAP:BOOL=OFF"
10 | "-DBUILD_TESTING:BOOL=OFF"
11 | "-DCMAKE_MODULE_PATH:PATH=${CMAKE_MODULE_PATH}"
12 | "-DOPENSSL_INCLUDE_DIR:PATH=${OPENSSL_INCLUDE_DIR}"
13 | "-DOPENSSL_SSL_LIBRARY:PATH=${OPENSSL_SSL_LIBRARY}"
14 | "-DOPENSSL_CRYPTO_LIBRARY:PATH=${OPENSSL_CRYPTO_LIBRARY}"
15 | "-DHAVE_POLL_FINE_EXITCODE:TEXT=1")
16 |
17 | set(CURL_STATIC_LIBRARIES ${CMAKE_BINARY_DIR}/ext/curl/lib/libcurl.a)
18 |
19 | ExternalProject_Add(
20 | curl_ext
21 | PREFIX curl
22 | URL "http://curl.haxx.se/download/curl-7.63.0.tar.gz"
23 | BUILD_BYPRODUCTS ${CURL_STATIC_LIBRARIES}
24 | INSTALL_DIR ${CMAKE_BINARY_DIR}/ext/curl
25 | CMAKE_ARGS ${CROSS_COMPILE_ARSG}
26 | CMAKE_CACHE_ARGS ${CURL_OPTIONS} ${CROSS_COMPILE_CACHE_ARGS}
27 | )
28 | add_dependencies(curl_ext boringssl)
29 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/ext/curl/include/)
30 | add_library(curl STATIC IMPORTED)
31 | add_dependencies(curl curl_ext)
32 | set_property(TARGET curl PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/ext/curl/lib/libcurl.a)
33 | set_property(TARGET curl PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_BINARY_DIR}/ext/curl/include/)
34 | set_property(TARGET curl PROPERTY INTERFACE_LINK_LIBRARIES boringssl z)
35 |
36 | set(CURL_FOUND TRUE)
37 | set(CURL_LIBRARY curl)
38 | set(CURL_LIBRARIES curl)
39 | set(CURL_INCLUDE_DIR ${CMAKE_BINARY_DIR}/ext/curl/include/)
40 | set(CURL_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/ext/curl/include/)
41 |
42 |
--------------------------------------------------------------------------------
/deps/libuv.cmake:
--------------------------------------------------------------------------------
1 | include(ExternalProject)
2 |
3 | set(LIBUV_OPTIONS "-DCMAKE_INSTALL_PREFIX:PATH=${CMAKE_BINARY_DIR}/ext/libuv"
4 | "-DBUILD_SHARED_LIBS:BOOL=OFF"
5 | "-DBUILD_TESTING:BOOL=OFF"
6 | "-DCMAKE_MODULE_PATH:PATH=${CMAKE_MODULE_PATH}")
7 |
8 | set(LIBUV_STATIC_LIBRARIES ${CMAKE_BINARY_DIR}/ext/libuv/lib/libuv_a.a)
9 |
10 | ExternalProject_Add(
11 | libuv_ext
12 | PREFIX libuv
13 | URL "https://github.com/libuv/libuv/archive/v1.37.0.tar.gz"
14 | BUILD_BYPRODUCTS ${LIBUV_STATIC_LIBRARIES}
15 | INSTALL_DIR ${CMAKE_BINARY_DIR}/ext/libuv
16 | CMAKE_ARGS ${CROSS_COMPILE_ARSG}
17 | CMAKE_CACHE_ARGS ${LIBUV_OPTIONS} ${CROSS_COMPILE_CACHE_ARGS}
18 | )
19 | file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/ext/libuv/include/)
20 | add_library(libuv STATIC IMPORTED)
21 | add_dependencies(libuv libuv_ext)
22 | set_property(TARGET libuv PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/ext/libuv/lib/libuv_a.a)
23 | set_property(TARGET libuv PROPERTY INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_BINARY_DIR}/ext/libuv/include/)
24 |
25 | set(LIBUV_FOUND TRUE)
26 | set(LIBUV_LIBRARIES libuv)
27 | set(LIBUV_INCLUDE_DIRS ${CMAKE_BINARY_DIR}/ext/libuv/include/)
28 |
29 |
--------------------------------------------------------------------------------
/deps/modules/FindCURL.cmake:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/deps/modules/FindCURL.cmake
--------------------------------------------------------------------------------
/deps/modules/FindOpenSSL.cmake:
--------------------------------------------------------------------------------
1 | #find_path(OPENSSL_INCLUDE_DIR
2 | # NAMES openssl/ssl.h
3 | # PATH_SUFFIXES include
4 | # HINTS ${OPENSSL_ROOT_DIR})
5 | #find_library(OPENSSL_SSL_LIBRARY
6 | # NAMES ssl
7 | # PATH_SUFFIXES lib
8 | # HINTS ${OPENSSL_ROOT_DIR})
9 | #find_library(OPENSSL_CRYPTO_LIBRARY
10 | # NAMES crypto
11 | # PATH_SUFFIXES lib
12 | # HINTS ${OPENSSL_ROOT_DIR})
13 |
14 | include(FindPackageHandleStandardArgs)
15 | find_package_handle_standard_args(OpenSSL DEFAULT_MSG
16 | OPENSSL_SSL_LIBRARY OPENSSL_CRYPTO_LIBRARY OPENSSL_INCLUDE_DIR)
17 |
18 | set(OPENSSL_LIBRARIES ${OPENSSL_SSL_LIBRARY} ${OPENSSL_CRYPTO_LIBRARY})
19 | set(OPENSSL_INCLUDE_DIRS ${OPENSSL_INCLUDE_DIR})
20 |
21 | add_library(OpenSSL::SSL UNKNOWN IMPORTED)
22 | set_target_properties(OpenSSL::SSL PROPERTIES
23 | INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
24 | set_target_properties(OpenSSL::SSL PROPERTIES
25 | IMPORTED_LINK_INTERFACE_LANGUAGES "C"
26 | IMPORTED_LOCATION "${OPENSSL_SSL_LIBRARY}")
27 |
28 | add_library(OpenSSL::Crypto UNKNOWN IMPORTED)
29 | set_target_properties(OpenSSL::SSL PROPERTIES
30 | INTERFACE_INCLUDE_DIRECTORIES "${OPENSSL_INCLUDE_DIR}")
31 | set_target_properties(OpenSSL::SSL PROPERTIES
32 | IMPORTED_LINK_INTERFACE_LANGUAGES "C"
33 | IMPORTED_LOCATION "${OPENSSL_CRYPTO_LIBRARY}")
--------------------------------------------------------------------------------
/deps/modules/FindProtobuf.cmake:
--------------------------------------------------------------------------------
1 |
2 | function(protobuf_generate)
3 | include(CMakeParseArguments)
4 |
5 | set(_options APPEND_PATH)
6 | set(_singleargs LANGUAGE OUT_VAR EXPORT_MACRO PROTOC_OUT_DIR)
7 | if(COMMAND target_sources)
8 | list(APPEND _singleargs TARGET)
9 | endif()
10 | set(_multiargs PROTOS IMPORT_DIRS GENERATE_EXTENSIONS)
11 |
12 | cmake_parse_arguments(protobuf_generate "${_options}" "${_singleargs}" "${_multiargs}" "${ARGN}")
13 |
14 | if(NOT protobuf_generate_PROTOS AND NOT protobuf_generate_TARGET)
15 | message(SEND_ERROR "Error: protobuf_generate called without any targets or source files")
16 | return()
17 | endif()
18 |
19 | if(NOT protobuf_generate_OUT_VAR AND NOT protobuf_generate_TARGET)
20 | message(SEND_ERROR "Error: protobuf_generate called without a target or output variable")
21 | return()
22 | endif()
23 |
24 | if(NOT protobuf_generate_LANGUAGE)
25 | set(protobuf_generate_LANGUAGE cpp)
26 | endif()
27 | string(TOLOWER ${protobuf_generate_LANGUAGE} protobuf_generate_LANGUAGE)
28 |
29 | if(NOT protobuf_generate_PROTOC_OUT_DIR)
30 | set(protobuf_generate_PROTOC_OUT_DIR ${CMAKE_CURRENT_BINARY_DIR})
31 | endif()
32 |
33 | if(protobuf_generate_EXPORT_MACRO AND protobuf_generate_LANGUAGE STREQUAL cpp)
34 | set(_dll_export_decl "dllexport_decl=${protobuf_generate_EXPORT_MACRO}:")
35 | endif()
36 |
37 | if(NOT protobuf_generate_GENERATE_EXTENSIONS)
38 | if(protobuf_generate_LANGUAGE STREQUAL cpp)
39 | set(protobuf_generate_GENERATE_EXTENSIONS .pb.h .pb.cc)
40 | elseif(protobuf_generate_LANGUAGE STREQUAL python)
41 | set(protobuf_generate_GENERATE_EXTENSIONS _pb2.py)
42 | else()
43 | message(SEND_ERROR "Error: protobuf_generate given unknown Language ${LANGUAGE}, please provide a value for GENERATE_EXTENSIONS")
44 | return()
45 | endif()
46 | endif()
47 |
48 | if(protobuf_generate_TARGET)
49 | get_target_property(_source_list ${protobuf_generate_TARGET} SOURCES)
50 | foreach(_file ${_source_list})
51 | if(_file MATCHES "proto$")
52 | list(APPEND protobuf_generate_PROTOS ${_file})
53 | endif()
54 | endforeach()
55 | endif()
56 |
57 | if(NOT protobuf_generate_PROTOS)
58 | message(SEND_ERROR "Error: protobuf_generate could not find any .proto files")
59 | return()
60 | endif()
61 |
62 | if(protobuf_generate_APPEND_PATH)
63 | # Create an include path for each file specified
64 | foreach(_file ${protobuf_generate_PROTOS})
65 | get_filename_component(_abs_file ${_file} ABSOLUTE)
66 | get_filename_component(_abs_path ${_abs_file} PATH)
67 | list(FIND _protobuf_include_path ${_abs_path} _contains_already)
68 | if(${_contains_already} EQUAL -1)
69 | list(APPEND _protobuf_include_path -I ${_abs_path})
70 | endif()
71 | endforeach()
72 | else()
73 | set(_protobuf_include_path -I ${CMAKE_CURRENT_SOURCE_DIR})
74 | endif()
75 |
76 | foreach(DIR ${protobuf_generate_IMPORT_DIRS})
77 | get_filename_component(ABS_PATH ${DIR} ABSOLUTE)
78 | list(FIND _protobuf_include_path ${ABS_PATH} _contains_already)
79 | if(${_contains_already} EQUAL -1)
80 | list(APPEND _protobuf_include_path -I ${ABS_PATH})
81 | endif()
82 | endforeach()
83 |
84 | set(_generated_srcs_all)
85 | foreach(_proto ${protobuf_generate_PROTOS})
86 | get_filename_component(_abs_file ${_proto} ABSOLUTE)
87 | get_filename_component(_basename ${_proto} NAME_WE)
88 |
89 | set(_generated_srcs)
90 | foreach(_ext ${protobuf_generate_GENERATE_EXTENSIONS})
91 | list(APPEND _generated_srcs "${protobuf_generate_PROTOC_OUT_DIR}/${_basename}${_ext}")
92 | endforeach()
93 | list(APPEND _generated_srcs_all ${_generated_srcs})
94 |
95 | add_custom_command(
96 | OUTPUT ${_generated_srcs}
97 | COMMAND protobuf::protoc
98 | ARGS --${protobuf_generate_LANGUAGE}_out ${_dll_export_decl}${protobuf_generate_PROTOC_OUT_DIR} ${_protobuf_include_path} ${_abs_file}
99 | DEPENDS ${_abs_file} protobuf::protoc
100 | COMMENT "Running ${protobuf_generate_LANGUAGE} protocol buffer compiler on ${_proto}"
101 | VERBATIM )
102 | endforeach()
103 |
104 | set_source_files_properties(${_generated_srcs_all} PROPERTIES GENERATED TRUE)
105 | if(protobuf_generate_OUT_VAR)
106 | set(${protobuf_generate_OUT_VAR} ${_generated_srcs_all} PARENT_SCOPE)
107 | endif()
108 | if(protobuf_generate_TARGET)
109 | target_sources(${protobuf_generate_TARGET} PRIVATE ${_generated_srcs_all})
110 | endif()
111 |
112 | endfunction()
113 |
114 | function(PROTOBUF_GENERATE_CPP SRCS HDRS)
115 | cmake_parse_arguments(protobuf_generate_cpp "" "EXPORT_MACRO" "" ${ARGN})
116 |
117 | set(_proto_files "${protobuf_generate_cpp_UNPARSED_ARGUMENTS}")
118 | if(NOT _proto_files)
119 | message(SEND_ERROR "Error: PROTOBUF_GENERATE_CPP() called without any proto files")
120 | return()
121 | endif()
122 |
123 | if(PROTOBUF_GENERATE_CPP_APPEND_PATH)
124 | set(_append_arg APPEND_PATH)
125 | endif()
126 |
127 | if(DEFINED Protobuf_IMPORT_DIRS)
128 | set(_import_arg IMPORT_DIRS ${Protobuf_IMPORT_DIRS})
129 | endif()
130 |
131 | set(_outvar)
132 | protobuf_generate(${_append_arg} LANGUAGE cpp EXPORT_MACRO ${protobuf_generate_cpp_EXPORT_MACRO} OUT_VAR _outvar ${_import_arg} PROTOS ${_proto_files})
133 |
134 | set(${SRCS})
135 | set(${HDRS})
136 | foreach(_file ${_outvar})
137 | if(_file MATCHES "cc$")
138 | list(APPEND ${SRCS} ${_file})
139 | else()
140 | list(APPEND ${HDRS} ${_file})
141 | endif()
142 | endforeach()
143 | set(${SRCS} ${${SRCS}} PARENT_SCOPE)
144 | set(${HDRS} ${${HDRS}} PARENT_SCOPE)
145 | endfunction()
146 |
147 | if(NOT DEFINED PROTOBUF_GENERATE_CPP_APPEND_PATH)
148 | set(PROTOBUF_GENERATE_CPP_APPEND_PATH TRUE)
149 | endif()
--------------------------------------------------------------------------------
/deps/protobuf.cmake:
--------------------------------------------------------------------------------
1 | set(protobuf_source_dir "${CMAKE_CURRENT_LIST_DIR}/protobuf")
2 | set(protobuf_SHARED_OR_STATIC STATIC)
3 | set(protobuf_VERSION "1.0.0")
4 |
5 | include(${CMAKE_CURRENT_LIST_DIR}/protobuf/cmake/libprotobuf-lite.cmake)
6 | include(${CMAKE_CURRENT_LIST_DIR}/protobuf/cmake/libprotobuf.cmake)
7 | target_link_libraries(libprotobuf PRIVATE log)
8 | target_compile_definitions(libprotobuf PRIVATE HAVE_PTHREAD)
9 | add_executable(protobuf::protoc IMPORTED)
10 | set_target_properties(protobuf::protoc PROPERTIES IMPORTED_LOCATION "${CMAKE_CURRENT_LIST_DIR}/protoc/protoc")
11 |
12 | set(Protobuf_LIBRARIES libprotobuf)
13 | set(Protobuf_INCLUDE_DIRS )
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx1536m
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app's APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Automatically convert third-party libraries to use AndroidX
19 | android.enableJetifier=true
20 |
21 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/MCMrARM/mc-android-version-switcher/71ef27304830bd051e78aae454d79dc570178cf1/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Fri May 01 17:29:27 CEST 2020
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name='MinecraftVersion'
3 |
--------------------------------------------------------------------------------