├── .github
└── ISSUE_TEMPLATE
│ ├── bug_report.md
│ └── feature_request.md
├── .gitignore
├── .gitmodules
├── .idea
└── copyright
│ ├── Geyser.xml
│ └── profiles_settings.xml
├── Jenkinsfile
├── LICENSE
├── README.md
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ ├── java
│ │ ├── awt
│ │ │ └── image
│ │ │ │ ├── BufferedImage.java
│ │ │ │ └── RenderedImage.java
│ │ └── util
│ │ │ └── Base64.java
│ ├── javax
│ │ └── imageio
│ │ │ └── ImageIO.java
│ └── org
│ │ └── geysermc
│ │ └── geyser
│ │ └── android
│ │ ├── MainActivity.java
│ │ ├── proxy
│ │ ├── PacketHandler.java
│ │ ├── PaletteManger.java
│ │ ├── Player.java
│ │ ├── ProxyLogger.java
│ │ └── ProxyServer.java
│ │ ├── service
│ │ └── ProxyService.java
│ │ ├── ui
│ │ ├── about
│ │ │ ├── AboutActivity.java
│ │ │ └── AboutFragment.java
│ │ ├── home
│ │ │ └── HomeFragment.java
│ │ ├── proxy
│ │ │ └── ProxyFragment.java
│ │ └── settings
│ │ │ ├── SettingsActivity.java
│ │ │ └── SettingsFragment.java
│ │ └── utils
│ │ ├── AndroidDeviceDump.java
│ │ ├── AndroidUtils.java
│ │ ├── ConfigUtils.java
│ │ ├── EventListeners.java
│ │ └── UserAuth.java
│ └── res
│ ├── drawable
│ ├── geyser_logo.png
│ ├── ic_add.xml
│ ├── ic_menu_discord.xml
│ ├── ic_menu_home.xml
│ ├── ic_menu_proxy.xml
│ ├── ic_menu_website.xml
│ ├── ic_notification_logs.xml
│ ├── nav_view_item_iconcolor.xml
│ ├── nav_view_item_shapecolor.xml
│ ├── nav_view_item_textcolor.xml
│ └── side_nav_bar.xml
│ ├── layout
│ ├── activity_about.xml
│ ├── activity_config_editor_advanced_pretty.xml
│ ├── activity_config_editor_advanced_raw.xml
│ ├── activity_config_editor_simple.xml
│ ├── activity_main.xml
│ ├── activity_settings.xml
│ ├── activity_user_auths.xml
│ ├── app_bar_main.xml
│ ├── content_main.xml
│ ├── dialog_user_auth.xml
│ ├── fragment_home.xml
│ ├── fragment_proxy.xml
│ └── nav_header_main.xml
│ ├── menu
│ ├── activity_main_drawer.xml
│ ├── config.xml
│ └── main.xml
│ ├── mipmap-hdpi
│ └── ic_launcher.png
│ ├── mipmap-mdpi
│ └── ic_launcher.png
│ ├── mipmap-xhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxhdpi
│ └── ic_launcher.png
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ ├── navigation
│ └── mobile_navigation.xml
│ ├── values
│ ├── arrays.xml
│ ├── colors.xml
│ ├── dimens.xml
│ ├── drawables.xml
│ ├── strings.xml
│ └── styles.xml
│ └── xml
│ ├── about_preferences.xml
│ └── root_preferences.xml
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── metadata
├── full_description.txt
├── images
│ ├── icon.png
│ └── phoneScreenshots
│ │ ├── 1.jpg
│ │ ├── 2.jpg
│ │ ├── 3.jpg
│ │ ├── 4.jpg
│ │ ├── 5.jpg
│ │ ├── 6.jpg
│ │ └── 7.jpg
├── short_description.txt
└── title.txt
└── settings.gradle
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
12 |
13 |
14 | **Describe the bug**
15 |
16 |
17 | **To Reproduce**
18 |
19 |
20 |
21 |
22 |
23 |
24 | **Expected behavior**
25 |
26 |
27 | **Screenshots / Videos**
28 |
29 |
30 | **Android OS Version**
31 |
32 |
33 | **Android App Version**
34 |
35 |
36 | **Geyser Version**
37 |
38 |
39 | **Minecraft: Bedrock Edition Version**
40 |
41 |
42 | **Additional Context**
43 |
44 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **What feature do you want?**
11 | Add a description
12 |
13 | **Alternatives?**
14 | Any alternatives you have tried
15 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Created by https://www.toptal.com/developers/gitignore/api/git,java,gradle,eclipse,netbeans,jetbrains+all,android
3 | # Edit at https://www.toptal.com/developers/gitignore?templates=git,java,gradle,eclipse,netbeans,jetbrains+all,android
4 |
5 | ### Android ###
6 | # Built application files
7 | *.apk
8 | *.aar
9 | *.ap_
10 | *.aab
11 |
12 | # Files for the ART/Dalvik VM
13 | *.dex
14 |
15 | # Java class files
16 | *.class
17 |
18 | # Generated files
19 | bin/
20 | gen/
21 | out/
22 | # Uncomment the following line in case you need and you don't have the release build type files in your app
23 | # release/
24 |
25 | # Gradle files
26 | .gradle/
27 | build/
28 |
29 | # Local configuration file (sdk path, etc)
30 | local.properties
31 |
32 | # Proguard folder generated by Eclipse
33 | proguard/
34 |
35 | # Log Files
36 | *.log
37 |
38 | # Android Studio Navigation editor temp files
39 | .navigation/
40 |
41 | # Android Studio captures folder
42 | captures/
43 |
44 | # IntelliJ
45 | *.iml
46 | .idea/workspace.xml
47 | .idea/tasks.xml
48 | .idea/gradle.xml
49 | .idea/assetWizardSettings.xml
50 | .idea/dictionaries
51 | .idea/libraries
52 | # Android Studio 3 in .gitignore file.
53 | .idea/caches
54 | .idea/modules.xml
55 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you
56 | .idea/navEditor.xml
57 |
58 | # Keystore files
59 | # Uncomment the following lines if you do not want to check your keystore files in.
60 | #*.jks
61 | #*.keystore
62 |
63 | # External native build folder generated in Android Studio 2.2 and later
64 | .externalNativeBuild
65 | .cxx/
66 |
67 | # Google Services (e.g. APIs or Firebase)
68 | # google-services.json
69 |
70 | # Freeline
71 | freeline.py
72 | freeline/
73 | freeline_project_description.json
74 |
75 | # fastlane
76 | fastlane/report.xml
77 | fastlane/Preview.html
78 | fastlane/screenshots
79 | fastlane/test_output
80 | fastlane/readme.md
81 |
82 | # Version control
83 | vcs.xml
84 |
85 | # lint
86 | lint/intermediates/
87 | lint/generated/
88 | lint/outputs/
89 | lint/tmp/
90 | # lint/reports/
91 |
92 | ### Android Patch ###
93 | gen-external-apklibs
94 | output.json
95 |
96 | # Replacement of .externalNativeBuild directories introduced
97 | # with Android Studio 3.5.
98 |
99 | ### Eclipse ###
100 | .metadata
101 | tmp/
102 | *.tmp
103 | *.bak
104 | *.swp
105 | *~.nib
106 | .settings/
107 | .loadpath
108 | .recommenders
109 |
110 | # External tool builders
111 | .externalToolBuilders/
112 |
113 | # Locally stored "Eclipse launch configurations"
114 | *.launch
115 |
116 | # PyDev specific (Python IDE for Eclipse)
117 | *.pydevproject
118 |
119 | # CDT-specific (C/C++ Development Tooling)
120 | .cproject
121 |
122 | # CDT- autotools
123 | .autotools
124 |
125 | # Java annotation processor (APT)
126 | .factorypath
127 |
128 | # PDT-specific (PHP Development Tools)
129 | .buildpath
130 |
131 | # sbteclipse plugin
132 | .target
133 |
134 | # Tern plugin
135 | .tern-project
136 |
137 | # TeXlipse plugin
138 | .texlipse
139 |
140 | # STS (Spring Tool Suite)
141 | .springBeans
142 |
143 | # Code Recommenders
144 | .recommenders/
145 |
146 | # Annotation Processing
147 | .apt_generated/
148 | .apt_generated_test/
149 |
150 | # Scala IDE specific (Scala & Java development for Eclipse)
151 | .cache-main
152 | .scala_dependencies
153 | .worksheet
154 |
155 | # Uncomment this line if you wish to ignore the project description file.
156 | # Typically, this file would be tracked if it contains build/dependency configurations:
157 | #.project
158 |
159 | ### Eclipse Patch ###
160 | # Spring Boot Tooling
161 | .sts4-cache/
162 |
163 | ### Git ###
164 | # Created by git for backups. To disable backups in Git:
165 | # $ git config --global mergetool.keepBackup false
166 | *.orig
167 |
168 | # Created by git when using merge tools for conflicts
169 | *.BACKUP.*
170 | *.BASE.*
171 | *.LOCAL.*
172 | *.REMOTE.*
173 | *_BACKUP_*.txt
174 | *_BASE_*.txt
175 | *_LOCAL_*.txt
176 | *_REMOTE_*.txt
177 |
178 | ### Java ###
179 | # Compiled class file
180 |
181 | # Log file
182 |
183 | # BlueJ files
184 | *.ctxt
185 |
186 | # Mobile Tools for Java (J2ME)
187 | .mtj.tmp/
188 |
189 | # Package Files #
190 | *.jar
191 | *.war
192 | *.nar
193 | *.ear
194 | *.zip
195 | *.tar.gz
196 | *.rar
197 |
198 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
199 | hs_err_pid*
200 |
201 | ### JetBrains+all ###
202 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
203 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
204 |
205 | # User-specific stuff
206 | .idea/**/workspace.xml
207 | .idea/**/tasks.xml
208 | .idea/**/usage.statistics.xml
209 | .idea/**/dictionaries
210 | .idea/**/shelf
211 |
212 | # Generated files
213 | .idea/**/contentModel.xml
214 |
215 | # Sensitive or high-churn files
216 | .idea/**/dataSources/
217 | .idea/**/dataSources.ids
218 | .idea/**/dataSources.local.xml
219 | .idea/**/sqlDataSources.xml
220 | .idea/**/dynamic.xml
221 | .idea/**/uiDesigner.xml
222 | .idea/**/dbnavigator.xml
223 |
224 | # Gradle
225 | .idea/**/gradle.xml
226 | .idea/**/libraries
227 |
228 | # Gradle and Maven with auto-import
229 | # When using Gradle or Maven with auto-import, you should exclude module files,
230 | # since they will be recreated, and may cause churn. Uncomment if using
231 | # auto-import.
232 | # .idea/artifacts
233 | # .idea/compiler.xml
234 | # .idea/jarRepositories.xml
235 | # .idea/modules.xml
236 | # .idea/*.iml
237 | # .idea/modules
238 | # *.iml
239 | # *.ipr
240 |
241 | # CMake
242 | cmake-build-*/
243 |
244 | # Mongo Explorer plugin
245 | .idea/**/mongoSettings.xml
246 |
247 | # File-based project format
248 | *.iws
249 |
250 | # IntelliJ
251 |
252 | # mpeltonen/sbt-idea plugin
253 | .idea_modules/
254 |
255 | # JIRA plugin
256 | atlassian-ide-plugin.xml
257 |
258 | # Cursive Clojure plugin
259 | .idea/replstate.xml
260 |
261 | # Crashlytics plugin (for Android Studio and IntelliJ)
262 | com_crashlytics_export_strings.xml
263 | crashlytics.properties
264 | crashlytics-build.properties
265 | fabric.properties
266 |
267 | # Editor-based Rest Client
268 | .idea/httpRequests
269 |
270 | # Android studio 3.1+ serialized cache file
271 | .idea/caches/build_file_checksums.ser
272 |
273 | ### JetBrains+all Patch ###
274 | # Ignores the whole .idea folder and all .iml files
275 | # See https://github.com/joeblau/gitignore.io/issues/186 and https://github.com/joeblau/gitignore.io/issues/360
276 |
277 | # The below was ajusted to add in the copyright file config
278 | .idea/*
279 | !.idea/copyright/
280 |
281 | # Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-249601023
282 |
283 | modules.xml
284 | .idea/misc.xml
285 | *.ipr
286 |
287 | # Sonarlint plugin
288 | .idea/sonarlint
289 |
290 | ### NetBeans ###
291 | **/nbproject/private/
292 | **/nbproject/Makefile-*.mk
293 | **/nbproject/Package-*.bash
294 | nbbuild/
295 | dist/
296 | nbdist/
297 | .nb-gradle/
298 |
299 | ### Gradle ###
300 | .gradle
301 |
302 | # Ignore Gradle GUI config
303 | gradle-app.setting
304 |
305 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored)
306 | !gradle-wrapper.jar
307 |
308 | # Cache of project
309 | .gradletasknamecache
310 |
311 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898
312 | # gradle/wrapper/gradle-wrapper.properties
313 |
314 | ### Gradle Patch ###
315 | **/build/
316 |
317 | # End of https://www.toptal.com/developers/gitignore/api/git,java,gradle,eclipse,netbeans,jetbrains+all,android
318 |
319 | app/release/
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "app/src/main/res-locale"]
2 | path = app/src/main/res-locale
3 | url = https://github.com/GeyserMC/languages.git
4 |
--------------------------------------------------------------------------------
/.idea/copyright/Geyser.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Jenkinsfile:
--------------------------------------------------------------------------------
1 | pipeline {
2 | agent {
3 | label 'master'
4 | }
5 |
6 | tools {
7 | gradle 'Gradle 6'
8 | jdk 'Java 8'
9 | }
10 |
11 | parameters{
12 | booleanParam(defaultValue: false, description: 'Skip Discord notification', name: 'SKIP_DISCORD')
13 | }
14 |
15 | options {
16 | buildDiscarder(logRotator(artifactNumToKeepStr: '20'))
17 | }
18 |
19 | stages {
20 | stage ('Build') {
21 | steps {
22 | sh 'git submodule update --init --recursive'
23 | sh 'gradle clean assembleRelease --refresh-dependencies'
24 | }
25 | }
26 | stage ('Sign') {
27 | steps {
28 | signAndroidApks (
29 | apksToSign: '**/*-unsigned.apk',
30 | keyStoreId: 'GeyserAndroidKeys',
31 | keyAlias: 'geyserandroid',
32 | archiveSignedApks: true
33 | )
34 | }
35 | }
36 | }
37 |
38 | post {
39 | always {
40 | script {
41 | def changeLogSets = currentBuild.changeSets
42 | def message = "**Changes:**"
43 |
44 | if (changeLogSets.size() == 0) {
45 | message += "\n*No changes.*"
46 | } else {
47 | def repositoryUrl = scm.userRemoteConfigs[0].url.replace(".git", "")
48 | def count = 0;
49 | def extra = 0;
50 | for (int i = 0; i < changeLogSets.size(); i++) {
51 | def entries = changeLogSets[i].items
52 | for (int j = 0; j < entries.length; j++) {
53 | if (count <= 10) {
54 | def entry = entries[j]
55 | def commitId = entry.commitId.substring(0, 6)
56 | message += "\n - [`${commitId}`](${repositoryUrl}/commit/${entry.commitId}) ${entry.msg}"
57 | count++
58 | } else {
59 | extra++;
60 | }
61 | }
62 | }
63 |
64 | if (extra != 0) {
65 | message += "\n - ${extra} more commits"
66 | }
67 | }
68 |
69 | env.changes = message
70 | }
71 | deleteDir()
72 | script {
73 | if(!params.SKIP_DISCORD) {
74 | withCredentials([string(credentialsId: 'geyser-discord-webhook', variable: 'DISCORD_WEBHOOK')]) {
75 | discordSend description: "**Build:** [${currentBuild.id}](${env.BUILD_URL})\n**Status:** [${currentBuild.currentResult}](${env.BUILD_URL})\n${changes}\n\n[**Artifacts on Jenkins**](https://ci.nukkitx.com/job/GeyserMC/job/GeyserAndroid/job/master/)", footer: 'Cloudburst Jenkins', link: env.BUILD_URL, successful: currentBuild.resultIsBetterOrEqualTo('SUCCESS'), title: "${env.JOB_NAME} #${currentBuild.id}", webhookURL: DISCORD_WEBHOOK
76 | }
77 | }
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 GeyserMC. http://geysermc.org
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Geyser Android
2 |
3 | [](https://java.com/)
4 |
5 | [](LICENSE)
6 | [](https://ci.opencollab.dev/job/GeyserMC/job/GeyserAndroid/job/master/)
7 | [](http://discord.geysermc.org/)
8 | [](http://hits.dwyl.io/GeyserMC/GeyserAndroid)
9 | [](https://trello.com/b/pPJpl9dZ/geyser-android)
10 |
11 | Geyser Android is an Android app version of Geyser.
12 |
13 | ## What is Geyser Android?
14 | Geyser Android is an Android app that supports a proxy server and running Geyser.
15 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | import com.github.jengelman.gradle.plugins.shadow.tasks.ShadowJar
2 |
3 | apply plugin: 'com.android.application'
4 | apply plugin: 'org.anarres.jarjar'
5 | apply plugin: "com.github.johnrengelman.shadow"
6 |
7 | android {
8 | compileSdkVersion 29
9 | buildToolsVersion "29.0.3"
10 |
11 | defaultConfig {
12 | applicationId "org.geysermc.geyser.android"
13 | minSdkVersion 24
14 | targetSdkVersion 29
15 | versionCode 103001 // Format 00.00.000 EG 1.0.1 -> 01.00.001 -> 0100001 -> 100001
16 | versionName "1.3.1"
17 |
18 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
19 | }
20 |
21 | buildTypes {
22 | release {
23 | minifyEnabled false
24 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
25 | }
26 |
27 | applicationVariants.all { variant ->
28 | variant.outputs.all { output ->
29 | def newName = outputFileName
30 |
31 | newName = newName.replace("app-", "${variant.mergedFlavor.applicationId}-")
32 | newName = newName.replace("-release", "-${variant.versionName}")
33 | newName = newName.replace("-debug", "-${variant.versionName}-debug")
34 |
35 | outputFileName = newName
36 | }
37 | }
38 | }
39 |
40 | compileOptions {
41 | coreLibraryDesugaringEnabled true
42 |
43 | sourceCompatibility JavaVersion.VERSION_1_8
44 | targetCompatibility JavaVersion.VERSION_1_8
45 | }
46 |
47 | packagingOptions {
48 | exclude 'META-INF/INDEX.LIST'
49 | exclude 'META-INF/io.netty.versions.properties'
50 | }
51 |
52 | configurations {
53 | nnio
54 | }
55 |
56 | sourceSets {
57 | main {
58 | res.srcDirs = [
59 | 'src/main/res',
60 | 'src/main/res-locale'
61 | ]
62 | }
63 | }
64 | }
65 |
66 | repositories {
67 | mavenLocal()
68 |
69 | maven {
70 | url 'https://repo.opencollab.dev/maven-releases/'
71 | }
72 |
73 | maven {
74 | url 'https://repo.opencollab.dev/maven-snapshots/'
75 | }
76 |
77 | maven {
78 | url 'https://jitpack.io'
79 | }
80 |
81 | maven {
82 | url 'https://oss.sonatype.org/content/repositories/snapshots/'
83 | }
84 |
85 | mavenCentral()
86 | }
87 |
88 | task nnioJar (type: ShadowJar) {
89 | getArchiveClassifier().set('nnio')
90 |
91 | relocate 'org.lukhnos.nnio.file', 'java.nio.file'
92 | relocate 'org.lukhnos.nnio.channels', 'java.nio.channels'
93 |
94 | configurations = [project.configurations.nnio]
95 | }
96 |
97 | dependencies {
98 | implementation fileTree(dir: "libs", include: ["*.jar"])
99 | implementation 'androidx.appcompat:appcompat:1.2.0'
100 | implementation 'androidx.legacy:legacy-support-v4:1.0.0'
101 | implementation 'com.google.android.material:material:1.3.0'
102 | implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
103 | implementation 'androidx.navigation:navigation-fragment:2.3.3'
104 | implementation 'androidx.navigation:navigation-ui:2.3.3'
105 | implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
106 | implementation 'androidx.preference:preference:1.1.1'
107 | implementation 'com.android.volley:volley:1.2.0'
108 |
109 | testImplementation 'junit:junit:4.12'
110 | androidTestImplementation 'androidx.test.ext:junit:1.1.2'
111 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
112 |
113 | coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5'
114 |
115 | compileOnly 'org.projectlombok:lombok:1.18.16'
116 | annotationProcessor 'org.projectlombok:lombok:1.18.16'
117 |
118 | implementation "com.fasterxml.jackson.core:jackson-databind:2.10.2"
119 |
120 | // Remove epoll, kqueue and fastutil from the protocol
121 | implementation('com.github.BedrockTogether:Protocol:f883229fa2') {
122 | exclude group: 'com.nukkitx.fastutil', module:'fastutil-common'
123 | exclude group: 'com.nukkitx.network', module:'raknet'
124 | exclude group: 'com.nukkitx.network', module:'common'
125 | exclude group: 'io.netty', module:'netty-transport-native-epoll'
126 | exclude group: 'io.netty', module:'netty-transport-native-kqueue'
127 | }
128 |
129 | // Include the updated common with the android fixes
130 | implementation 'com.nukkitx.network:common:1.6.26-20210217.205834-2'
131 | implementation 'com.nukkitx.network:raknet:1.6.26-20210217.205834-2'
132 |
133 | // Implement the cleaned fastutil manually to prevent the error on release builds
134 | implementation files("${buildDir}/jarjar/fastutil-cleaned.jar") { builtBy 'jarjar-repackage_fastutil-cleaned.jar' }
135 |
136 | // Remove the duplicate classes and include fastutil-common
137 | compileOnly jarjar.repackage('fastutil-cleaned.jar') {
138 | from 'com.nukkitx.fastutil:fastutil-common:8.5.2'
139 |
140 | // classDelete "it.unimi.dsi.fastutil.ints.IntIterator"
141 | // classDelete "it.unimi.dsi.fastutil.longs.LongIterator"
142 | // classDelete "it.unimi.dsi.fastutil.objects.ObjectIterator"
143 | // classDelete "it.unimi.dsi.fastutil.booleans.BooleanIterator"
144 | // classDelete "it.unimi.dsi.fastutil.bytes.ByteIterator"
145 | // classDelete "it.unimi.dsi.fastutil.doubles.DoubleIterator"
146 | // classDelete "it.unimi.dsi.fastutil.floats.FloatIterator"
147 | }
148 |
149 | // Load a remake of nnio to support earlier Android versions
150 | nnio 'com.github.rtm516:nnio:c7b291f4ca'
151 | implementation tasks.nnioJar.outputs.files
152 |
153 | // slf4j port for Android
154 | implementation 'org.slf4j:slf4j-android:1.7.30'
155 | }
--------------------------------------------------------------------------------
/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
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
16 |
19 |
22 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/app/src/main/java/java/awt/image/BufferedImage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package java.awt.image;
27 |
28 | import android.graphics.Bitmap;
29 |
30 | import lombok.Getter;
31 |
32 | import static android.graphics.Bitmap.Config.ARGB_8888;
33 |
34 | /**
35 | * This class is a facade used by Geyser because
36 | * BufferedImage does not exist on Android.
37 | * So we implement it based on the Bitmap class.
38 | */
39 | @SuppressWarnings({"unused", "RedundantSuppression"})
40 | public class BufferedImage extends RenderedImage {
41 |
42 | public static final int TYPE_INT_ARGB = 2;
43 |
44 | @Getter
45 | private Bitmap bitmap;
46 |
47 | public BufferedImage(Bitmap bitmap) {
48 | this.bitmap = bitmap;
49 | }
50 |
51 | public BufferedImage(int width, int height, int format) {
52 | if (format != TYPE_INT_ARGB) {
53 | throw new IllegalArgumentException("BufferedImage format must be TYPE_INT_ARGB!");
54 | }
55 |
56 | this.bitmap = Bitmap.createBitmap(width, height, ARGB_8888);
57 | }
58 |
59 | public int getWidth() {
60 | return bitmap.getWidth();
61 | }
62 |
63 | public int getHeight() {
64 | return bitmap.getHeight();
65 | }
66 |
67 | public void setRGB(int x, int y, int color) {
68 | bitmap.setPixel(x, y, color);
69 | }
70 |
71 | public int getRGB(int x, int y) {
72 | return bitmap.getPixel(x, y);
73 | }
74 |
75 | public void flush() {
76 | bitmap.recycle();
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/app/src/main/java/java/awt/image/RenderedImage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package java.awt.image;
27 |
28 | /**
29 | * This is a dummy class just used for a method declaration
30 | */
31 | public class RenderedImage {
32 | }
33 |
--------------------------------------------------------------------------------
/app/src/main/java/java/util/Base64.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package java.util;
27 |
28 | import lombok.Getter;
29 |
30 | /**
31 | * This class is a facade used for support of earlier android versions.
32 | * java.util.Base64 wasn't included until API level 26, so this
33 | * class redirects the methods used to the android.util.Base64 class.
34 | */
35 | @SuppressWarnings({"unused", "RedundantSuppression"})
36 | public class Base64 {
37 |
38 | @Getter
39 | public static Encoder encoder = new Encoder();
40 | @Getter
41 | public static Decoder decoder = new Decoder();
42 |
43 | public static class Encoder {
44 | public String encodeToString(byte[] src) {
45 | return android.util.Base64.encodeToString(src, android.util.Base64.NO_WRAP);
46 | }
47 | }
48 |
49 | public static class Decoder {
50 | public byte[] decode(String src) {
51 | return android.util.Base64.decode(src, android.util.Base64.NO_WRAP);
52 | }
53 | }
54 |
55 | }
56 |
--------------------------------------------------------------------------------
/app/src/main/java/javax/imageio/ImageIO.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package javax.imageio;
27 |
28 | import android.graphics.Bitmap;
29 | import android.graphics.BitmapFactory;
30 |
31 | import java.awt.image.BufferedImage;
32 | import java.awt.image.RenderedImage;
33 | import java.io.File;
34 | import java.io.FileInputStream;
35 | import java.io.FileOutputStream;
36 | import java.io.IOException;
37 | import java.io.InputStream;
38 | import java.net.URL;
39 |
40 | /**
41 | * This class is a facade used by Geyser because
42 | * ImageIO does not exist on Android.
43 | */
44 | @SuppressWarnings({"unused", "RedundantSuppression"})
45 | public class ImageIO {
46 |
47 | public static BufferedImage read(URL input) throws IOException {
48 | if (input == null) {
49 | throw new IllegalArgumentException("input == null!");
50 | }
51 |
52 | InputStream inputStream;
53 | try {
54 | inputStream = input.openStream();
55 | } catch (IOException e) {
56 | throw new IOException("Can't get input stream from URL!", e);
57 | }
58 |
59 | return read(inputStream);
60 | }
61 |
62 | public static BufferedImage read(File input) throws IOException {
63 | if (input == null) {
64 | throw new IllegalArgumentException("input == null!");
65 | }
66 | if (!input.canRead()) {
67 | throw new IOException("Can't read input file!");
68 | }
69 |
70 | return read(new FileInputStream(input));
71 | }
72 |
73 | public static BufferedImage read(InputStream input) throws IOException {
74 | if (input == null) {
75 | throw new IllegalArgumentException("input == null!");
76 | }
77 |
78 | // if (input.available() == 0) {
79 | // return null;
80 | // }
81 |
82 | return new BufferedImage(BitmapFactory.decodeStream(input));
83 | }
84 |
85 | @SuppressWarnings("SameReturnValue")
86 | public static boolean write(RenderedImage image, String format, File file) throws IOException {
87 | if (!format.equals("png")) {
88 | throw new IllegalArgumentException("ImageIO.write format must be png!");
89 | }
90 | if (!(image instanceof BufferedImage)) {
91 | throw new IllegalArgumentException("ImageIO.write image must be BufferedImage!");
92 | }
93 |
94 | FileOutputStream out = new FileOutputStream(file);
95 | ((BufferedImage) image).getBitmap().compress(Bitmap.CompressFormat.PNG, 100, out);
96 | out.close();
97 |
98 | return true;
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/MainActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android;
27 |
28 | import android.content.Context;
29 | import android.content.Intent;
30 | import android.os.Bundle;
31 | import android.view.Menu;
32 | import android.view.MenuItem;
33 |
34 | import androidx.annotation.NonNull;
35 | import androidx.appcompat.app.AppCompatActivity;
36 | import androidx.appcompat.app.AppCompatDelegate;
37 | import androidx.appcompat.widget.Toolbar;
38 | import androidx.core.view.GravityCompat;
39 | import androidx.drawerlayout.widget.DrawerLayout;
40 | import androidx.navigation.NavController;
41 | import androidx.navigation.Navigation;
42 | import androidx.navigation.ui.AppBarConfiguration;
43 | import androidx.navigation.ui.NavigationUI;
44 | import androidx.preference.PreferenceManager;
45 |
46 | import com.google.android.material.navigation.NavigationView;
47 |
48 | import org.geysermc.geyser.android.ui.about.AboutActivity;
49 | import org.geysermc.geyser.android.ui.settings.SettingsActivity;
50 | import org.geysermc.geyser.android.utils.AndroidUtils;
51 |
52 | import lombok.Getter;
53 |
54 | public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
55 |
56 | private AppBarConfiguration mAppBarConfiguration;
57 | private DrawerLayout drawer;
58 | private NavController navController;
59 |
60 | @Getter
61 | private static Context context;
62 |
63 | @Override
64 | protected void onCreate(Bundle savedInstanceState) {
65 | super.onCreate(savedInstanceState);
66 |
67 | // Set the static context
68 | context = this;
69 |
70 | setContentView(R.layout.activity_main);
71 | setTitle(getResources().getString(R.string.menu_home));
72 |
73 | // Setup the theme
74 | String theme = PreferenceManager.getDefaultSharedPreferences(getContext()).getString("theme", "system");
75 | if (theme != null) {
76 | switch (theme) {
77 | case "dark":
78 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
79 | break;
80 |
81 | case "light":
82 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
83 | break;
84 |
85 | default:
86 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
87 | break;
88 | }
89 | }
90 |
91 | // Setup the action bar
92 | Toolbar toolbar = findViewById(R.id.toolbar);
93 | setSupportActionBar(toolbar);
94 | drawer = findViewById(R.id.drawer_layout);
95 | NavigationView navigationView = findViewById(R.id.nav_view);
96 | // Passing each menu ID as a set of Ids because each
97 | // menu should be considered as top level destinations.
98 | mAppBarConfiguration = new AppBarConfiguration.Builder(
99 | R.id.nav_home, R.id.nav_proxy)
100 | .setOpenableLayout(drawer)
101 | .build();
102 | navController = Navigation.findNavController(this, R.id.nav_host_fragment);
103 | NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
104 | NavigationUI.setupWithNavController(navigationView, navController);
105 |
106 | // Setup the navigation listener
107 | setNavigationViewListener();
108 |
109 | // Disable epoll kqueue from loading
110 | System.getProperties().put("disableNativeEventLoop", "disableNativeEventLoop");
111 |
112 | Intent intent = getIntent();
113 | int navElement = intent.getIntExtra("nav_element", -1);
114 | if (navElement != -1) {
115 | NavigationUI.onNavDestinationSelected(navigationView.getMenu().findItem(navElement), navController);
116 | }
117 | }
118 |
119 | @Override
120 | public boolean onCreateOptionsMenu(Menu menu) {
121 | // Inflate the menu; this adds items to the action bar if it is present.
122 | getMenuInflater().inflate(R.menu.main, menu);
123 | return true;
124 | }
125 |
126 | @Override
127 | public boolean onOptionsItemSelected(@NonNull MenuItem item) {
128 | switch (item.getItemId()) {
129 | case R.id.action_settings:
130 | Intent settingsIntent = new Intent(MainActivity.this, SettingsActivity.class);
131 | MainActivity.this.startActivity(settingsIntent);
132 | return true;
133 | case R.id.action_about:
134 | Intent aboutIntent = new Intent(MainActivity.this, AboutActivity.class);
135 | MainActivity.this.startActivity(aboutIntent);
136 | return true;
137 | default:
138 | break;
139 | }
140 |
141 | return super.onOptionsItemSelected(item);
142 | }
143 |
144 | @Override
145 | public boolean onSupportNavigateUp() {
146 | NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
147 | return NavigationUI.navigateUp(navController, mAppBarConfiguration)
148 | || super.onSupportNavigateUp();
149 | }
150 |
151 | private void setNavigationViewListener() {
152 | NavigationView navigationView = findViewById(R.id.nav_view);
153 | navigationView.setNavigationItemSelectedListener(this);
154 | }
155 |
156 | @Override
157 | public boolean onNavigationItemSelected(@NonNull MenuItem item) {
158 | switch (item.getItemId()) {
159 | case R.id.nav_website:
160 | AndroidUtils.showURL(getResources().getString(R.string.app_site));
161 | drawer.closeDrawer(GravityCompat.START);
162 | return false;
163 | case R.id.nav_discord:
164 | AndroidUtils.showURL(getResources().getString(R.string.app_discord));
165 | drawer.closeDrawer(GravityCompat.START);
166 | return false;
167 | }
168 |
169 | // Pass the select even onto the nav ui
170 | boolean handled = NavigationUI.onNavDestinationSelected(item, navController);
171 |
172 | // Close the drawer if it was handled
173 | if (handled) {
174 | drawer.closeDrawer(GravityCompat.START);
175 | }
176 |
177 | return handled;
178 | }
179 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/proxy/PacketHandler.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.proxy;
27 |
28 | import com.fasterxml.jackson.databind.JsonNode;
29 | import com.fasterxml.jackson.databind.node.JsonNodeType;
30 | import com.nimbusds.jose.JWSObject;
31 | import com.nimbusds.jose.crypto.factories.DefaultJWSVerifierFactory;
32 | import com.nukkitx.network.util.DisconnectReason;
33 | import com.nukkitx.protocol.bedrock.BedrockServerSession;
34 | import com.nukkitx.protocol.bedrock.handler.BedrockPacketHandler;
35 | import com.nukkitx.protocol.bedrock.packet.LoginPacket;
36 | import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
37 | import com.nukkitx.protocol.bedrock.packet.ResourcePackClientResponsePacket;
38 | import com.nukkitx.protocol.bedrock.packet.ResourcePackStackPacket;
39 | import com.nukkitx.protocol.bedrock.packet.ResourcePacksInfoPacket;
40 | import com.nukkitx.protocol.bedrock.packet.SetLocalPlayerAsInitializedPacket;
41 | import com.nukkitx.protocol.bedrock.util.EncryptionUtils;
42 |
43 | import java.io.IOException;
44 | import java.security.interfaces.ECPublicKey;
45 |
46 | import static org.geysermc.geyser.android.utils.AndroidUtils.OBJECT_MAPPER;
47 |
48 | public class PacketHandler implements BedrockPacketHandler {
49 |
50 | private final BedrockServerSession session;
51 | private final ProxyServer masterServer;
52 |
53 | private Player player;
54 |
55 | public PacketHandler(BedrockServerSession session, ProxyServer masterServer) {
56 | this.session = session;
57 | this.masterServer = masterServer;
58 |
59 | session.addDisconnectHandler(this::disconnect);
60 | }
61 |
62 | public void disconnect(DisconnectReason reason) {
63 | if (player != null) {
64 | masterServer.getProxyLogger().info(player.getDisplayName() + " has disconnected from the master server (" + reason + ")");
65 | masterServer.getPlayers().remove(player.getXuid());
66 | }
67 | }
68 |
69 | @Override
70 | public boolean handle(LoginPacket packet) {
71 | // Check the protocol version is correct
72 | int protocol = packet.getProtocolVersion();
73 | if (protocol != ProxyServer.CODEC.getProtocolVersion()) {
74 | PlayStatusPacket status = new PlayStatusPacket();
75 | if (protocol > ProxyServer.CODEC.getProtocolVersion()) {
76 | status.setStatus(PlayStatusPacket.Status.LOGIN_FAILED_SERVER_OLD);
77 | } else {
78 | status.setStatus(PlayStatusPacket.Status.LOGIN_FAILED_CLIENT_OLD);
79 | }
80 | session.sendPacket(status);
81 | }
82 |
83 | // Set the session codec
84 | session.setPacketCodec(ProxyServer.CODEC);
85 |
86 | // Read the raw chain data
87 | JsonNode rawChainData;
88 | try {
89 | rawChainData = OBJECT_MAPPER.readTree(packet.getChainData().toByteArray());
90 | } catch (IOException e) {
91 | throw new AssertionError("Unable to read chain data!");
92 | }
93 |
94 | // Get the parsed chain data
95 | JsonNode chainData = rawChainData.get("chain");
96 | if (chainData.getNodeType() != JsonNodeType.ARRAY) {
97 | throw new AssertionError("Invalid chain data!");
98 | }
99 |
100 | try {
101 | // Parse the signed jws object
102 | JWSObject jwsObject;
103 | jwsObject = JWSObject.parse(chainData.get(chainData.size() - 1).asText());
104 |
105 | // Read the JWS payload
106 | JsonNode payload = OBJECT_MAPPER.readTree(jwsObject.getPayload().toBytes());
107 |
108 | // Check the identityPublicKey is there
109 | if (payload.get("identityPublicKey").getNodeType() != JsonNodeType.STRING) {
110 | throw new AssertionError("Missing identity public key!");
111 | }
112 |
113 | // Create an ECPublicKey from the identityPublicKey
114 | ECPublicKey identityPublicKey = EncryptionUtils.generateKey(payload.get("identityPublicKey").textValue());
115 |
116 | // Get the skin data to validate the JWS token
117 | JWSObject skinData = JWSObject.parse(packet.getSkinData().toString());
118 | if (skinData.verify(new DefaultJWSVerifierFactory().createJWSVerifier(skinData.getHeader(), identityPublicKey))) {
119 | // Make sure the client sent over the username, xuid and other info
120 | if (payload.get("extraData").getNodeType() != JsonNodeType.OBJECT) {
121 | throw new AssertionError("Missing client data");
122 | }
123 |
124 | // Fetch the client data
125 | JsonNode extraData = payload.get("extraData");
126 |
127 | // Create a new player and add it to the players list
128 | player = new Player(extraData, session);
129 | masterServer.getPlayers().put(player.getXuid(), player);
130 |
131 | // Tell the client we have logged in successfully
132 | PlayStatusPacket playStatusPacket = new PlayStatusPacket();
133 | playStatusPacket.setStatus(PlayStatusPacket.Status.LOGIN_SUCCESS);
134 | session.sendPacket(playStatusPacket);
135 |
136 | // Tell the client there are no resourcepacks
137 | ResourcePacksInfoPacket resourcePacksInfo = new ResourcePacksInfoPacket();
138 | session.sendPacket(resourcePacksInfo);
139 | } else {
140 | throw new AssertionError("Invalid identity public key!");
141 | }
142 | } catch (Exception e) {
143 | // Disconnect the client
144 | session.disconnect("disconnectionScreen.internalError.cantConnect");
145 | throw new AssertionError("Failed to login", e);
146 | }
147 |
148 | return false;
149 | }
150 |
151 | @Override
152 | public boolean handle(ResourcePackClientResponsePacket packet) {
153 | switch (packet.getStatus()) {
154 | case COMPLETED:
155 | masterServer.getProxyLogger().info("Logged in " + player.getDisplayName() + " (" + player.getXuid() + ", " + player.getIdentity() + ")");
156 | player.sendStartGame();
157 | break;
158 | case HAVE_ALL_PACKS:
159 | ResourcePackStackPacket stack = new ResourcePackStackPacket();
160 | stack.setExperimentsPreviouslyToggled(false);
161 | stack.setForcedToAccept(false);
162 | stack.setGameVersion("*");
163 | session.sendPacket(stack);
164 | break;
165 | default:
166 | session.disconnect("disconnectionScreen.resourcePack");
167 | break;
168 | }
169 |
170 | return true;
171 | }
172 |
173 | @Override
174 | public boolean handle(SetLocalPlayerAsInitializedPacket packet) {
175 | masterServer.getProxyLogger().debug("Player initialized: " + player.getDisplayName());
176 |
177 | player.connectToServer(ProxyServer.getInstance().getAddress(), ProxyServer.getInstance().getPort());
178 |
179 | return false;
180 | }
181 | }
182 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/proxy/PaletteManger.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.proxy;
27 |
28 | import com.nukkitx.nbt.NBTOutputStream;
29 | import com.nukkitx.nbt.NbtList;
30 | import com.nukkitx.nbt.NbtMap;
31 | import com.nukkitx.nbt.NbtMapBuilder;
32 | import com.nukkitx.nbt.NbtUtils;
33 |
34 | import java.io.ByteArrayOutputStream;
35 | import java.io.IOException;
36 |
37 | public class PaletteManger {
38 |
39 | public static final NbtMap BIOMES_PALETTE;
40 | public static final byte[] EMPTY_LEVEL_CHUNK_DATA;
41 |
42 | private static final NbtMap EMPTY_TAG = NbtMap.EMPTY;
43 |
44 | static {
45 | /* Load biomes */
46 | // Build a fake plains biome entry
47 | NbtMapBuilder plainsBuilder = NbtMap.builder();
48 | plainsBuilder.putFloat("blue_spores", 0f);
49 | plainsBuilder.putFloat("white_ash", 0f);
50 | plainsBuilder.putFloat("ash", 0f);
51 | plainsBuilder.putFloat("temperature", 0f);
52 | plainsBuilder.putFloat("red_spores", 0f);
53 | plainsBuilder.putFloat("downfall", 0f);
54 |
55 | plainsBuilder.put("minecraft:overworld_generation_rules", NbtMap.EMPTY);
56 | plainsBuilder.put("minecraft:climate", NbtMap.EMPTY);
57 | plainsBuilder.put("tags", NbtList.EMPTY);
58 |
59 | // Add the fake plains to the map
60 | NbtMapBuilder biomesBuilder = NbtMap.builder();
61 | biomesBuilder.put("plains", plainsBuilder.build());
62 |
63 | // Build the biomes palette
64 | BIOMES_PALETTE = biomesBuilder.build();
65 |
66 | /* Create empty chunk data */
67 | try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
68 | outputStream.write(new byte[258]); // Biomes + Border Size + Extra Data Size
69 |
70 | try (NBTOutputStream nbtOutputStream = NbtUtils.createNetworkWriter(outputStream)) {
71 | nbtOutputStream.writeTag(EMPTY_TAG);
72 | }
73 |
74 | EMPTY_LEVEL_CHUNK_DATA = outputStream.toByteArray();
75 | } catch (IOException e) {
76 | throw new AssertionError("Unable to generate empty level chunk data");
77 | }
78 | }
79 |
80 | @SuppressWarnings("EmptyMethod")
81 | public static void init() {
82 | // no-op
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/proxy/Player.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.proxy;
27 |
28 | import com.fasterxml.jackson.databind.JsonNode;
29 | import com.nukkitx.math.vector.Vector2f;
30 | import com.nukkitx.math.vector.Vector3f;
31 | import com.nukkitx.math.vector.Vector3i;
32 | import com.nukkitx.protocol.bedrock.BedrockServerSession;
33 | import com.nukkitx.protocol.bedrock.data.AuthoritativeMovementMode;
34 | import com.nukkitx.protocol.bedrock.data.GamePublishSetting;
35 | import com.nukkitx.protocol.bedrock.data.GameRuleData;
36 | import com.nukkitx.protocol.bedrock.data.GameType;
37 | import com.nukkitx.protocol.bedrock.data.PlayerPermission;
38 | import com.nukkitx.protocol.bedrock.data.SyncedPlayerMovementSettings;
39 | import com.nukkitx.protocol.bedrock.data.inventory.ItemData;
40 | import com.nukkitx.protocol.bedrock.packet.BiomeDefinitionListPacket;
41 | import com.nukkitx.protocol.bedrock.packet.CreativeContentPacket;
42 | import com.nukkitx.protocol.bedrock.packet.LevelChunkPacket;
43 | import com.nukkitx.protocol.bedrock.packet.PlayStatusPacket;
44 | import com.nukkitx.protocol.bedrock.packet.SetEntityMotionPacket;
45 | import com.nukkitx.protocol.bedrock.packet.StartGamePacket;
46 | import com.nukkitx.protocol.bedrock.packet.TransferPacket;
47 |
48 | import java.net.InetSocketAddress;
49 | import java.util.UUID;
50 |
51 | import lombok.Getter;
52 |
53 | @Getter
54 | public class Player {
55 |
56 | private final String xuid;
57 | private final UUID identity;
58 | private final String displayName;
59 |
60 | private final BedrockServerSession session;
61 |
62 | public Player(JsonNode extraData, BedrockServerSession session) {
63 | this.xuid = extraData.get("XUID").asText();
64 | this.identity = UUID.fromString(extraData.get("identity").asText());
65 | this.displayName = extraData.get("displayName").asText();
66 |
67 | this.session = session;
68 | }
69 |
70 | /**
71 | * Send a few different packets to get the client to load in
72 | */
73 | public void sendStartGame() {
74 | // A lot of this likely doesn't need to be changed
75 | StartGamePacket startGamePacket = new StartGamePacket();
76 | startGamePacket.setUniqueEntityId(1);
77 | startGamePacket.setRuntimeEntityId(1);
78 | startGamePacket.setPlayerGameType(GameType.DEFAULT);
79 | startGamePacket.setPlayerPosition(Vector3f.from(0, 64 + 2, 0));
80 | startGamePacket.setRotation(Vector2f.ONE);
81 |
82 | startGamePacket.setSeed(-1);
83 | startGamePacket.setDimensionId(2);
84 | startGamePacket.setGeneratorId(1);
85 | startGamePacket.setLevelGameType(GameType.DEFAULT);
86 | startGamePacket.setDifficulty(0);
87 | startGamePacket.setDefaultSpawn(Vector3i.ZERO);
88 | startGamePacket.setAchievementsDisabled(true);
89 | startGamePacket.setCurrentTick(-1);
90 |
91 | startGamePacket.setEduEditionOffers(0);
92 | startGamePacket.setEduFeaturesEnabled(false);
93 | startGamePacket.setRainLevel(0);
94 | startGamePacket.setLightningLevel(0);
95 |
96 | startGamePacket.setMultiplayerGame(true);
97 | startGamePacket.setBroadcastingToLan(true);
98 | startGamePacket.getGamerules().add(new GameRuleData<>("showcoordinates", true));
99 | startGamePacket.setPlatformBroadcastMode(GamePublishSetting.PUBLIC);
100 | startGamePacket.setXblBroadcastMode(GamePublishSetting.PUBLIC);
101 | startGamePacket.setCommandsEnabled(true);
102 | startGamePacket.setTexturePacksRequired(false);
103 | startGamePacket.setBonusChestEnabled(false);
104 | startGamePacket.setStartingWithMap(false);
105 | startGamePacket.setTrustingPlayers(true);
106 | startGamePacket.setDefaultPlayerPermission(PlayerPermission.VISITOR);
107 | startGamePacket.setServerChunkTickRange(4);
108 | startGamePacket.setBehaviorPackLocked(false);
109 | startGamePacket.setResourcePackLocked(false);
110 | startGamePacket.setFromLockedWorldTemplate(false);
111 | startGamePacket.setUsingMsaGamertagsOnly(false);
112 | startGamePacket.setFromWorldTemplate(false);
113 | startGamePacket.setWorldTemplateOptionLocked(false);
114 |
115 | SyncedPlayerMovementSettings settings = new SyncedPlayerMovementSettings();
116 | settings.setMovementMode(AuthoritativeMovementMode.CLIENT);
117 | settings.setRewindHistorySize(0);
118 | settings.setServerAuthoritativeBlockBreaking(false);
119 | startGamePacket.setPlayerMovementSettings(settings);
120 | startGamePacket.setVanillaVersion("1.17.40");
121 |
122 | startGamePacket.setLevelId("world");
123 | startGamePacket.setLevelName("world");
124 | startGamePacket.setPremiumWorldTemplateId("00000000-0000-0000-0000-000000000000");
125 | startGamePacket.setCurrentTick(0);
126 | startGamePacket.setEnchantmentSeed(0);
127 | startGamePacket.setMultiplayerCorrelationId("");
128 | startGamePacket.setServerEngine("");
129 |
130 | session.sendPacket(startGamePacket);
131 |
132 | // Send an empty chunk
133 | LevelChunkPacket data = new LevelChunkPacket();
134 | data.setChunkX(0);
135 | data.setChunkZ(0);
136 | data.setSubChunksLength(0);
137 | data.setData(PaletteManger.EMPTY_LEVEL_CHUNK_DATA);
138 | data.setCachingEnabled(false);
139 | session.sendPacket(data);
140 |
141 | // Send a CreativeContentPacket - required for 1.16.100
142 | CreativeContentPacket creativeContentPacket = new CreativeContentPacket();
143 | creativeContentPacket.setContents(new ItemData[0]);
144 | session.sendPacket(creativeContentPacket);
145 |
146 | // Send the biomes
147 | BiomeDefinitionListPacket biomeDefinitionListPacket = new BiomeDefinitionListPacket();
148 | biomeDefinitionListPacket.setDefinitions(PaletteManger.BIOMES_PALETTE);
149 | session.sendPacket(biomeDefinitionListPacket);
150 |
151 | // Let the client know the player can spawn
152 | PlayStatusPacket playStatusPacket = new PlayStatusPacket();
153 | playStatusPacket.setStatus(PlayStatusPacket.Status.PLAYER_SPAWN);
154 | session.sendPacket(playStatusPacket);
155 |
156 | // Freeze the player
157 | SetEntityMotionPacket setEntityMotionPacket = new SetEntityMotionPacket();
158 | setEntityMotionPacket.setRuntimeEntityId(1);
159 | setEntityMotionPacket.setMotion(Vector3f.ZERO);
160 | session.sendPacket(setEntityMotionPacket);
161 | }
162 |
163 | /**
164 | * Send the player to the another server
165 | */
166 | public void connectToServer(String address, int port) {
167 | ProxyServer.getInstance().getProxyLogger().info("Sending server transfer packet to " + displayName);
168 |
169 | // Create an InetSocketAddress to reduce issues with hostnames for PS4
170 | // Thanks Extollite
171 | InetSocketAddress socketAddress = new InetSocketAddress(address, port);
172 |
173 | TransferPacket transferPacket = new TransferPacket();
174 | transferPacket.setAddress(socketAddress.getAddress().getHostAddress());
175 | transferPacket.setPort(socketAddress.getPort());
176 | session.sendPacket(transferPacket);
177 | }
178 | }
179 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/proxy/ProxyLogger.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.proxy;
27 |
28 | import org.geysermc.geyser.android.utils.EventListeners;
29 |
30 | import lombok.Getter;
31 | import lombok.Setter;
32 |
33 | public class ProxyLogger {
34 |
35 | @Getter
36 | private static String log = "";
37 |
38 | @Setter
39 | private static EventListeners.LogEventListener listener;
40 |
41 | public void warning(String message) {
42 | log += "WARN - " + message + "\n";
43 | if (listener != null) listener.onLogLine("WARN - " + message);
44 | // System.out.println("WARN - " + message);
45 | }
46 |
47 | public void info(String message) {
48 | log += "INFO - " + message + "\n";
49 | if (listener != null) listener.onLogLine("INFO - " + message);
50 | // System.out.println("INFO - " + message);
51 | }
52 |
53 | public void error(String message, Throwable error) {
54 | log += "ERROR - " + message + "\n";
55 | if (listener != null) listener.onLogLine("ERROR - " + message);
56 | // System.out.println("ERROR - " + message + " - " + error.getMessage());
57 | // error.printStackTrace();
58 | }
59 |
60 | public void debug(String message) {
61 | log += "DEBUG - " + message + "\n";
62 | if (listener != null) listener.onLogLine ("DEBUG - " + message);
63 | // System.out.println("DEBUG - " + message);
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/proxy/ProxyServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.proxy;
27 |
28 | import android.content.Context;
29 |
30 | import com.nukkitx.protocol.bedrock.BedrockPacketCodec;
31 | import com.nukkitx.protocol.bedrock.BedrockPong;
32 | import com.nukkitx.protocol.bedrock.BedrockServer;
33 | import com.nukkitx.protocol.bedrock.BedrockServerEventHandler;
34 | import com.nukkitx.protocol.bedrock.BedrockServerSession;
35 | import com.nukkitx.protocol.bedrock.v431.Bedrock_v431;
36 | import com.nukkitx.protocol.bedrock.v440.Bedrock_v440;
37 | import com.nukkitx.protocol.bedrock.v475.Bedrock_v475;
38 |
39 | import org.geysermc.geyser.android.R;
40 | import org.geysermc.geyser.android.utils.EventListeners;
41 |
42 | import java.net.InetSocketAddress;
43 | import java.util.ArrayList;
44 | import java.util.HashMap;
45 | import java.util.List;
46 | import java.util.Map;
47 | import java.util.Timer;
48 | import java.util.TimerTask;
49 | import java.util.concurrent.Executors;
50 | import java.util.concurrent.ScheduledExecutorService;
51 |
52 | import lombok.Getter;
53 |
54 | public class ProxyServer {
55 |
56 | public static final BedrockPacketCodec CODEC = Bedrock_v475.V475_CODEC;
57 |
58 | private BedrockServer bdServer;
59 | private BedrockPong bdPong;
60 |
61 | @Getter
62 | private boolean shuttingDown = false;
63 |
64 | @Getter
65 | private static ProxyServer instance;
66 |
67 | @Getter
68 | private ProxyLogger proxyLogger;
69 |
70 | @Getter
71 | private ScheduledExecutorService generalThreadPool;
72 |
73 | @Getter
74 | private final Map players = new HashMap<>();
75 |
76 | @Getter
77 | private final String address;
78 |
79 | @Getter
80 | private final int port;
81 |
82 | @Getter
83 | private final Context ctx;
84 |
85 | @Getter
86 | private static final List onDisableListeners = new ArrayList<>();
87 |
88 | public ProxyServer(String address, int port, Context ctx) {
89 | this.address = address;
90 | this.port = port;
91 | this.ctx = ctx;
92 | }
93 |
94 | public void onEnable() {
95 | instance = this;
96 |
97 | proxyLogger = new ProxyLogger();
98 |
99 | this.generalThreadPool = Executors.newScheduledThreadPool(32);
100 |
101 | // Start a timer to keep the thread running
102 | Timer timer = new Timer();
103 | TimerTask task = new TimerTask() { public void run() { } };
104 | timer.scheduleAtFixedRate(task, 0L, 1000L);
105 |
106 | // Initialise the palettes
107 | PaletteManger.init();
108 |
109 | start();
110 | }
111 |
112 | public void onDisable() {
113 | this.shutdown();
114 |
115 | for (EventListeners.OnDisableEventListener onDisableListener : onDisableListeners) {
116 | if (onDisableListener != null) onDisableListener.onDisable();
117 | }
118 | }
119 |
120 | private void start() {
121 | proxyLogger.info(ctx.getResources().getString(R.string.proxy_starting) + "...");
122 |
123 | InetSocketAddress bindAddress = new InetSocketAddress("0.0.0.0", 19132);
124 | bdServer = new BedrockServer(bindAddress);
125 |
126 | bdPong = new BedrockPong();
127 | bdPong.setEdition("MCPE");
128 | bdPong.setMotd(ctx.getResources().getString(R.string.menu_proxy));
129 | bdPong.setSubMotd(ctx.getResources().getString(R.string.menu_proxy));
130 | bdPong.setPlayerCount(0);
131 | bdPong.setMaximumPlayerCount(1337);
132 | bdPong.setGameType("Survival");
133 | bdPong.setIpv4Port(19132);
134 | bdPong.setProtocolVersion(ProxyServer.CODEC.getProtocolVersion());
135 | bdPong.setVersion(ProxyServer.CODEC.getMinecraftVersion());
136 |
137 | bdServer.setHandler(new BedrockServerEventHandler() {
138 | @Override
139 | public boolean onConnectionRequest(InetSocketAddress address) {
140 | return true; // Connection will be accepted
141 | }
142 |
143 | @Override
144 | public BedrockPong onQuery(InetSocketAddress address) {
145 | return bdPong;
146 | }
147 |
148 | @Override
149 | public void onSessionCreation(BedrockServerSession session) {
150 | session.setPacketHandler(new PacketHandler(session, instance));
151 | }
152 | });
153 |
154 | // Start server up
155 | bdServer.bind().join();
156 | proxyLogger.info(String.format(ctx.getResources().getString(R.string.proxy_started), "0.0.0.0:19132"));
157 | }
158 |
159 | public void shutdown() {
160 | proxyLogger.info(ctx.getResources().getString(R.string.proxy_shutdown));
161 | shuttingDown = true;
162 |
163 | bdServer.close();
164 | generalThreadPool.shutdown();
165 | instance = null;
166 | proxyLogger.info(ctx.getResources().getString(R.string.proxy_shutdown_done));
167 | }
168 | }
169 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/service/ProxyService.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.service;
27 |
28 | import android.app.Notification;
29 | import android.app.NotificationChannel;
30 | import android.app.NotificationManager;
31 | import android.app.PendingIntent;
32 | import android.app.Service;
33 | import android.content.Intent;
34 | import android.content.SharedPreferences;
35 | import android.os.Build;
36 | import android.os.IBinder;
37 |
38 | import androidx.annotation.Nullable;
39 | import androidx.core.app.NotificationCompat;
40 | import androidx.preference.PreferenceManager;
41 |
42 | import org.geysermc.geyser.android.MainActivity;
43 | import org.geysermc.geyser.android.R;
44 | import org.geysermc.geyser.android.proxy.ProxyServer;
45 | import org.geysermc.geyser.android.utils.EventListeners;
46 |
47 | import lombok.Getter;
48 | import lombok.Setter;
49 |
50 | public class ProxyService extends Service {
51 |
52 | private final int NOTIFCATION_ID = 1338;
53 | private final String ACTION_STOP_SERVICE = "STOP_PROXY_SERVICE";
54 |
55 | private ProxyServer proxy;
56 |
57 | @Getter
58 | private static boolean finishedStartup;
59 |
60 | @Setter
61 | private static EventListeners.StartedEventListener listener;
62 |
63 | @Override
64 | public void onCreate() {
65 | super.onCreate();
66 |
67 | finishedStartup = false;
68 |
69 | Intent openLogs = new Intent(this, MainActivity.class);
70 | openLogs.putExtra("nav_element", R.id.nav_proxy);
71 |
72 | PendingIntent openLogsPendingIntent = PendingIntent.getActivity(this, NOTIFCATION_ID + 1, openLogs, 0);
73 |
74 | Intent stopSelf = new Intent(this, ProxyService.class);
75 | stopSelf.setAction(this.ACTION_STOP_SERVICE);
76 |
77 | PendingIntent stopPendingIntent = PendingIntent.getService(this, NOTIFCATION_ID + 2, stopSelf, PendingIntent.FLAG_CANCEL_CURRENT);
78 |
79 | Notification notification = new NotificationCompat.Builder(this, "proxy_channel")
80 | .setSmallIcon(R.drawable.ic_menu_proxy)
81 | .setContentTitle(getResources().getString(R.string.proxy_background_notification))
82 | .addAction(R.drawable.ic_notification_logs, getResources().getString(R.string.proxy_logs), openLogsPendingIntent)
83 | .addAction(R.drawable.ic_menu_manage, getResources().getString(R.string.proxy_stop), stopPendingIntent)
84 | .build();
85 |
86 | createNotificationChannel();
87 | startForeground(NOTIFCATION_ID, notification);
88 |
89 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
90 | ProxyServer.getOnDisableListeners().add(this::stopSelf);
91 | proxy = new ProxyServer(sharedPreferences.getString("proxy_address", getResources().getString(R.string.default_ip)), Integer.parseInt(sharedPreferences.getString("proxy_port", getResources().getString(R.string.default_port_be))), this);
92 | }
93 |
94 | @Override
95 | public void onDestroy() {
96 | super.onDestroy();
97 |
98 | ProxyServer.getInstance().onDisable();
99 | }
100 |
101 | @Override
102 | public int onStartCommand(Intent intent, int flags, int startId) {
103 | if (ACTION_STOP_SERVICE.equals(intent.getAction())) {
104 | if (finishedStartup) {
105 | stopSelf();
106 | }
107 | } else {
108 | Runnable runnable = () -> {
109 | try {
110 | proxy.onEnable();
111 | if (listener != null) listener.onStarted(false);
112 | finishedStartup = true;
113 | } catch (Exception e) {
114 | if (listener != null) listener.onStarted(true);
115 | stopForeground(true);
116 | }
117 | };
118 | Thread proxyThread = new Thread(runnable);
119 | proxyThread.start();
120 | }
121 |
122 | return super.onStartCommand(intent, flags, startId);
123 | }
124 |
125 | @Nullable
126 | @Override
127 | public IBinder onBind(Intent intent) {
128 | return null;
129 | }
130 |
131 | private void createNotificationChannel() {
132 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
133 | NotificationChannel notificationChannel = new NotificationChannel("proxy_channel", getResources().getString(R.string.menu_proxy), NotificationManager.IMPORTANCE_DEFAULT);
134 | NotificationManager notificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
135 | notificationManager.createNotificationChannel(notificationChannel);
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/ui/about/AboutActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.ui.about;
27 |
28 | import android.os.Bundle;
29 | import android.view.MenuItem;
30 |
31 | import androidx.appcompat.app.ActionBar;
32 | import androidx.appcompat.app.AppCompatActivity;
33 |
34 | import org.geysermc.geyser.android.R;
35 |
36 | public class AboutActivity extends AppCompatActivity {
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_about);
42 | getSupportFragmentManager()
43 | .beginTransaction()
44 | .replace(R.id.about, new AboutFragment())
45 | .commit();
46 | ActionBar actionBar = getSupportActionBar();
47 | if (actionBar != null) {
48 | actionBar.setDisplayHomeAsUpEnabled(true);
49 | }
50 | }
51 |
52 | @Override
53 | public boolean onOptionsItemSelected(MenuItem item) {
54 | // The back button
55 | if (item.getItemId() == android.R.id.home) {
56 | super.onBackPressed();
57 | return true;
58 | }
59 | return super.onOptionsItemSelected(item);
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/ui/about/AboutFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.ui.about;
27 |
28 | import android.os.Bundle;
29 |
30 | import androidx.preference.Preference;
31 | import androidx.preference.PreferenceFragmentCompat;
32 |
33 | import org.geysermc.geyser.android.BuildConfig;
34 | import org.geysermc.geyser.android.R;
35 | import org.geysermc.geyser.android.utils.AndroidUtils;
36 |
37 | public class AboutFragment extends PreferenceFragmentCompat {
38 |
39 | @Override
40 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
41 | setPreferencesFromResource(R.xml.about_preferences, rootKey);
42 |
43 | findPreference("version").setSummary(BuildConfig.VERSION_NAME);
44 | }
45 |
46 | @Override
47 | public boolean onPreferenceTreeClick(Preference preference) {
48 | switch (preference.getKey()) {
49 | // About
50 | case "version":
51 | AndroidUtils.setClipboard(requireContext(), BuildConfig.VERSION_NAME);
52 | AndroidUtils.showToast(getContext(), getResources().getString(R.string.about_version_copied));
53 | return true;
54 |
55 | // Links
56 | case "github":
57 | AndroidUtils.showURL("https://github.com/GeyserMC/GeyserAndroid");
58 | return true;
59 | case "trello":
60 | AndroidUtils.showURL("https://trello.com/b/pPJpl9dZ/geyser-android");
61 | return true;
62 | case "ci":
63 | AndroidUtils.showURL("https://ci.opencollab.dev/job/GeyserMC/job/GeyserAndroid/");
64 | return true;
65 |
66 | // Credits
67 | case "rtm516":
68 | AndroidUtils.showURL("https://rtm516.co.uk/");
69 | return true;
70 | case "arcratist":
71 | AndroidUtils.showURL("https://github.com/Arcratist/");
72 | return true;
73 | case "geyser":
74 | AndroidUtils.showURL(getResources().getString(R.string.app_site));
75 | return true;
76 |
77 | // Licences
78 | case "fontawesome":
79 | AndroidUtils.showURL("https://fontawesome.com/license");
80 | return true;
81 | default:
82 | break;
83 | }
84 |
85 | return super.onPreferenceTreeClick(preference);
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/ui/home/HomeFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.ui.home;
27 |
28 | import android.os.Bundle;
29 | import android.view.LayoutInflater;
30 | import android.view.Menu;
31 | import android.view.View;
32 | import android.view.ViewGroup;
33 | import android.widget.Button;
34 |
35 | import androidx.annotation.NonNull;
36 | import androidx.fragment.app.Fragment;
37 | import androidx.navigation.NavController;
38 | import androidx.navigation.fragment.NavHostFragment;
39 | import androidx.navigation.ui.NavigationUI;
40 |
41 | import com.google.android.material.navigation.NavigationView;
42 |
43 | import org.geysermc.geyser.android.R;
44 |
45 | public class HomeFragment extends Fragment {
46 |
47 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
48 | View root = inflater.inflate(R.layout.fragment_home, container, false);
49 |
50 | Button btnJoinBE = root.findViewById(R.id.btnJoinBE);
51 |
52 | // Get the menu and nav controller
53 | Menu menu = ((NavigationView) requireActivity().findViewById(R.id.nav_view)).getMenu();
54 | NavController navController = ((NavHostFragment) getParentFragment()).getNavController();
55 |
56 | // Setup the join BE button
57 | btnJoinBE.setOnClickListener(v -> NavigationUI.onNavDestinationSelected(menu.findItem(R.id.nav_proxy), navController));
58 |
59 | return root;
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/ui/proxy/ProxyFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.ui.proxy;
27 |
28 | import android.content.Intent;
29 | import android.content.SharedPreferences;
30 | import android.os.Bundle;
31 | import android.text.method.ScrollingMovementMethod;
32 | import android.view.LayoutInflater;
33 | import android.view.View;
34 | import android.view.ViewGroup;
35 | import android.widget.Button;
36 | import android.widget.TextView;
37 |
38 | import androidx.annotation.NonNull;
39 | import androidx.core.content.ContextCompat;
40 | import androidx.fragment.app.Fragment;
41 | import androidx.preference.PreferenceManager;
42 |
43 | import org.geysermc.geyser.android.BuildConfig;
44 | import org.geysermc.geyser.android.R;
45 | import org.geysermc.geyser.android.proxy.ProxyLogger;
46 | import org.geysermc.geyser.android.proxy.ProxyServer;
47 | import org.geysermc.geyser.android.service.ProxyService;
48 | import org.geysermc.geyser.android.utils.AndroidUtils;
49 |
50 | public class ProxyFragment extends Fragment {
51 |
52 | private SharedPreferences sharedPreferences;
53 |
54 | private TextView txtAddress;
55 | private TextView txtPort;
56 | private Button btnStartStop;
57 | private TextView txtLogs;
58 |
59 | public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
60 | View root = inflater.inflate(R.layout.fragment_proxy, container, false);
61 |
62 | sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
63 | txtAddress = root.findViewById(R.id.txtAddress);
64 | txtPort = root.findViewById(R.id.txtPort);
65 | btnStartStop = root.findViewById(R.id.btnStartStop);
66 | txtLogs = root.findViewById(R.id.txtLogs);
67 |
68 | // Set the movement method for the logs
69 | txtLogs.setMovementMethod(new ScrollingMovementMethod());
70 |
71 | // Set the initial text for all the UI elements
72 | txtLogs.setText(ProxyLogger.getLog());
73 | txtAddress.setText(sharedPreferences.getString("proxy_address", getResources().getString(R.string.default_ip)));
74 | txtPort.setText(sharedPreferences.getString("proxy_port", getResources().getString(R.string.default_port_be)));
75 |
76 | // Check if the server is already running
77 | if (ProxyServer.getInstance() != null && !ProxyServer.getInstance().isShuttingDown()) {
78 | // Check if the server is still starting
79 | if (ProxyService.isFinishedStartup()) {
80 | btnStartStop.setText(container.getResources().getString(R.string.proxy_stop));
81 | } else {
82 | btnStartStop.setText(container.getResources().getString(R.string.proxy_starting));
83 | btnStartStop.setEnabled(false);
84 | }
85 |
86 | txtAddress.setEnabled(false);
87 | txtPort.setEnabled(false);
88 |
89 | // Setup the listeners for the current screen
90 | setupListeners(container);
91 | }
92 |
93 | // Update the preference when the user has finished changing
94 | txtAddress.addTextChangedListener(AndroidUtils.generateAfterTextChange((editable) -> sharedPreferences.edit().putString("proxy_address", editable.toString()).apply()));
95 |
96 | // Update the preference when the user has finished changing
97 | txtPort.addTextChangedListener(AndroidUtils.generateAfterTextChange((editable) -> sharedPreferences.edit().putString("proxy_port", editable.toString()).apply()));
98 |
99 | btnStartStop.setOnClickListener(v -> {
100 | Button self = (Button) v;
101 | if (ProxyServer.getInstance() != null && !ProxyServer.getInstance().isShuttingDown()) {
102 | Intent serviceIntent = new Intent(getContext(), ProxyService.class);
103 | getContext().stopService(serviceIntent);
104 |
105 | self.setText(container.getResources().getString(R.string.proxy_start));
106 | txtAddress.setEnabled(true);
107 | txtPort.setEnabled(true);
108 | } else {
109 | self.setText(container.getResources().getString(R.string.proxy_starting));
110 | self.setEnabled(false);
111 | txtAddress.setEnabled(false);
112 | txtPort.setEnabled(false);
113 |
114 | // Clear all the current disable listeners to preserve memory usage
115 | ProxyServer.getOnDisableListeners().clear();
116 |
117 | // Setup the listeners for the current screen
118 | setupListeners(container);
119 |
120 | // Start the proxy service
121 | Intent serviceIntent = new Intent(getContext(), ProxyService.class);
122 | ContextCompat.startForegroundService(getContext(), serviceIntent);
123 | }
124 | });
125 |
126 | return root;
127 | }
128 |
129 | /**
130 | * Setup the listeners for all the events of the logger and service
131 | *
132 | * @param container The container to use for getting resources
133 | */
134 | private void setupListeners(ViewGroup container) {
135 | // When we have a new log line add it to txtLogs
136 | ProxyLogger.setListener(line -> {
137 | if (txtLogs != null) {
138 | // If we are in debug then print logs to the console aswell
139 | if (BuildConfig.DEBUG) {
140 | System.out.println(AndroidUtils.purgeColorCodes(line));
141 | }
142 |
143 | AndroidUtils.runOnUiThread(getActivity(), () -> txtLogs.append(AndroidUtils.purgeColorCodes(line) + "\n"));
144 | }
145 | });
146 |
147 | // When the server is disabled toggle the button
148 | ProxyServer.getOnDisableListeners().add(() -> AndroidUtils.runOnUiThread(getActivity(), () -> {
149 | btnStartStop.setText(container.getResources().getString(R.string.proxy_start));
150 | txtAddress.setEnabled(true);
151 | txtPort.setEnabled(true);
152 | }));
153 |
154 | // When the server has started and its failed status
155 | ProxyService.setListener((failed) -> AndroidUtils.runOnUiThread(getActivity(), () -> {
156 | if (failed) {
157 | btnStartStop.setText(container.getResources().getString(R.string.proxy_start));
158 | btnStartStop.setEnabled(true);
159 | txtAddress.setEnabled(true);
160 | txtPort.setEnabled(true);
161 | } else {
162 | btnStartStop.setText(container.getResources().getString(R.string.proxy_stop));
163 | btnStartStop.setEnabled(true);
164 | }
165 | }));
166 | }
167 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/ui/settings/SettingsActivity.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.ui.settings;
27 |
28 | import android.os.Bundle;
29 | import android.view.MenuItem;
30 |
31 | import androidx.appcompat.app.ActionBar;
32 | import androidx.appcompat.app.AppCompatActivity;
33 |
34 | import org.geysermc.geyser.android.R;
35 |
36 | public class SettingsActivity extends AppCompatActivity {
37 |
38 | @Override
39 | protected void onCreate(Bundle savedInstanceState) {
40 | super.onCreate(savedInstanceState);
41 | setContentView(R.layout.activity_settings);
42 | getSupportFragmentManager()
43 | .beginTransaction()
44 | .replace(R.id.settings, new SettingsFragment())
45 | .commit();
46 | ActionBar actionBar = getSupportActionBar();
47 | if (actionBar != null) {
48 | actionBar.setDisplayHomeAsUpEnabled(true);
49 | }
50 | }
51 |
52 | @Override
53 | public boolean onOptionsItemSelected(MenuItem item) {
54 | // The back button
55 | if (item.getItemId() == android.R.id.home) {
56 | super.onBackPressed();
57 | return true;
58 | }
59 | return super.onOptionsItemSelected(item);
60 | }
61 | }
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/ui/settings/SettingsFragment.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.ui.settings;
27 |
28 | import android.annotation.SuppressLint;
29 | import android.content.SharedPreferences;
30 | import android.os.Bundle;
31 | import android.text.InputType;
32 | import android.widget.EditText;
33 | import android.widget.Toast;
34 |
35 | import androidx.appcompat.app.AlertDialog;
36 | import androidx.appcompat.app.AppCompatDelegate;
37 | import androidx.preference.Preference;
38 | import androidx.preference.PreferenceFragmentCompat;
39 | import androidx.preference.PreferenceManager;
40 |
41 | import com.android.volley.ClientError;
42 | import com.android.volley.Request;
43 | import com.android.volley.RequestQueue;
44 | import com.android.volley.toolbox.JsonObjectRequest;
45 | import com.android.volley.toolbox.Volley;
46 | import com.fasterxml.jackson.core.JsonProcessingException;
47 | import com.fasterxml.jackson.databind.ObjectMapper;
48 |
49 | import org.geysermc.geyser.android.R;
50 | import org.geysermc.geyser.android.utils.AndroidDeviceDump;
51 | import org.geysermc.geyser.android.utils.AndroidUtils;
52 | import org.json.JSONException;
53 | import org.json.JSONObject;
54 |
55 | import java.io.File;
56 |
57 | public class SettingsFragment extends PreferenceFragmentCompat {
58 |
59 | private static final ObjectMapper MAPPER = new ObjectMapper();
60 | private static final String DUMP_URL = "https://dump.geysermc.org/";
61 |
62 | @SuppressLint("NewApi")
63 | @Override
64 | public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
65 | setPreferencesFromResource(R.xml.root_preferences, rootKey);
66 |
67 | Preference configReset = findPreference("geyser_reset_config");
68 | configReset.setOnPreferenceClickListener(preference -> {
69 | File configFile = AndroidUtils.getStoragePath(requireContext()).resolve("config.yml").toFile();
70 | if (configFile.exists()) {
71 | if (configFile.delete()) {
72 | new AlertDialog.Builder(requireContext())
73 | .setTitle(getResources().getString(R.string.settings_reset_config_success_title))
74 | .setMessage(getResources().getString(R.string.settings_reset_config_success_message))
75 | .setPositiveButton(android.R.string.ok, null)
76 | .show();
77 | } else {
78 | new AlertDialog.Builder(requireContext())
79 | .setTitle(getResources().getString(R.string.settings_reset_config_failed_title))
80 | .setMessage(getResources().getString(R.string.settings_reset_config_failed_message))
81 | .setPositiveButton(android.R.string.ok, null)
82 | .show();
83 | }
84 | } else {
85 | new AlertDialog.Builder(requireContext())
86 | .setTitle(getResources().getString(R.string.settings_reset_config_missing_title))
87 | .setMessage(getResources().getString(R.string.settings_reset_config_missing_message))
88 | .setPositiveButton(android.R.string.ok, null)
89 | .show();
90 | }
91 | return true;
92 | });
93 |
94 | Preference userAuthsReset = findPreference("geyser_reset_user_auths");
95 | userAuthsReset.setOnPreferenceClickListener(preference -> {
96 | SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireContext());
97 |
98 | if (!("{}".equals(sharedPreferences.getString("geyser_user_auths", "{}")))) {
99 | sharedPreferences.edit().putString("geyser_user_auths", "{}").apply();
100 |
101 | new AlertDialog.Builder(requireContext())
102 | .setTitle(getResources().getString(R.string.settings_reset_user_auths_success_title))
103 | .setMessage(getResources().getString(R.string.settings_reset_user_auths_success_message))
104 | .setPositiveButton(android.R.string.ok, null)
105 | .show();
106 | } else {
107 | new AlertDialog.Builder(getContext())
108 | .setTitle(getResources().getString(R.string.settings_reset_user_auths_missing_title))
109 | .setMessage(getResources().getString(R.string.settings_reset_user_auths_missing_message))
110 | .setPositiveButton(android.R.string.ok, null)
111 | .show();
112 | }
113 | return true;
114 | });
115 |
116 | // Handle the dump creation prefrence
117 | Preference createDeviceDump = findPreference("create_device_dump");
118 | createDeviceDump.setOnPreferenceClickListener(preference -> {
119 | try {
120 | // Let the user know we started
121 | AndroidUtils.showToast(getContext(), getResources().getString(R.string.settings_create_device_dump_uploading), Toast.LENGTH_LONG);
122 |
123 | // Dump the device info to JSON
124 | JSONObject data = new JSONObject(MAPPER.writeValueAsString(new AndroidDeviceDump(getContext())));
125 |
126 | // Create the request
127 | JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.POST, DUMP_URL + "documents", data, response -> {
128 | try {
129 | // Check if we got a response key
130 | if (!response.has("key")) {
131 | new AlertDialog.Builder(getContext())
132 | .setTitle(getResources().getString(R.string.settings_create_device_dump_failed_title))
133 | .setMessage(getResources().getString(R.string.settings_create_device_dump_failed_message, (response.has("message") ? response.getString("message") : response.toString())))
134 | .setPositiveButton(android.R.string.ok, (dialog, which) -> {})
135 | .show();
136 | }
137 |
138 | String uploadedDumpUrl = DUMP_URL + response.getString("key");
139 |
140 | final EditText input = new EditText(getContext());
141 | input.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_URI);
142 | input.setText(uploadedDumpUrl);
143 |
144 | new AlertDialog.Builder(getContext())
145 | .setTitle(getResources().getString(R.string.settings_create_device_dump_success_title))
146 | .setView(input)
147 | .setPositiveButton(android.R.string.ok, (dialog, which) -> {})
148 | .setNegativeButton(getResources().getString(R.string.settings_create_device_dump_open), (dialog, which) -> {
149 | AndroidUtils.showURL(uploadedDumpUrl);
150 | })
151 | .show();
152 | } catch (JSONException e) {
153 | new AlertDialog.Builder(getContext())
154 | .setTitle(getResources().getString(R.string.settings_create_device_dump_failed_title))
155 | .setMessage(getResources().getString(R.string.settings_create_device_dump_failed_message, e.getMessage()))
156 | .setPositiveButton(android.R.string.ok, (dialog, which) -> {})
157 | .show();
158 | }
159 | }, error -> {
160 | String message = error.getMessage();
161 | try {
162 | JSONObject response = new JSONObject(new String(((ClientError) error).networkResponse.data));
163 | message = response.getString("message");
164 | } catch (JSONException ignored) { }
165 |
166 | new AlertDialog.Builder(getContext())
167 | .setTitle(getResources().getString(R.string.settings_create_device_dump_failed_title))
168 | .setMessage(getResources().getString(R.string.settings_create_device_dump_failed_message, message))
169 | .setPositiveButton(android.R.string.ok, (dialog, which) -> {})
170 | .show();
171 | });
172 |
173 | RequestQueue queue = Volley.newRequestQueue(getContext());
174 | queue.add(jsonObjectRequest);
175 | } catch (JsonProcessingException | JSONException e) {
176 | new AlertDialog.Builder(getContext())
177 | .setTitle(getResources().getString(R.string.settings_create_device_dump_failed_title))
178 | .setMessage(getResources().getString(R.string.settings_create_device_dump_failed_message, e.getMessage()))
179 | .setPositiveButton(android.R.string.ok, (dialog, which) -> {})
180 | .show();
181 | }
182 |
183 | return true;
184 | });
185 |
186 | PreferenceManager.getDefaultSharedPreferences(requireContext()).registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> {
187 | if (key.equals("theme")) {
188 | String theme = sharedPreferences.getString(key, "system");
189 | assert theme != null;
190 | switch (theme) {
191 | case "dark":
192 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);
193 | break;
194 |
195 | case "light":
196 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);
197 | break;
198 |
199 | default:
200 | AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);
201 | break;
202 | }
203 | }
204 | });
205 | }
206 | }
207 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/utils/AndroidDeviceDump.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2021 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.utils;
27 |
28 | import android.content.Context;
29 | import android.content.pm.PackageInfo;
30 | import android.content.pm.PackageManager;
31 | import android.os.Build;
32 |
33 | import lombok.Getter;
34 |
35 | @Getter
36 | public class AndroidDeviceDump {
37 |
38 | private final AndroidDeviceDump.AndroidInfo androidInfo;
39 | private final AndroidDeviceDump.AppInfo appInfo;
40 |
41 | public AndroidDeviceDump(Context ctx) {
42 | this.androidInfo = new AndroidDeviceDump.AndroidInfo();
43 | this.appInfo = new AndroidDeviceDump.AppInfo(ctx);
44 | }
45 |
46 | @Getter
47 | public static class AndroidInfo {
48 |
49 | public final String androidVersion;
50 | public final int androidAPIVersion;
51 | public final String deviceManufacturer;
52 | public final String deviceModel;
53 |
54 | public AndroidInfo() {
55 | androidVersion = Build.VERSION.RELEASE;
56 | androidAPIVersion = Build.VERSION.SDK_INT;
57 | deviceManufacturer = Build.MANUFACTURER;
58 | deviceModel = Build.MODEL;
59 | }
60 | }
61 |
62 | @Getter
63 | public static class AppInfo {
64 |
65 | public long versionCode = 0;
66 | public String versionName = "Unknown";
67 |
68 | public AppInfo(Context ctx) {
69 | try {
70 | PackageInfo packageInfo = ctx.getPackageManager().getPackageInfo(ctx.getPackageName(), 0);
71 | versionCode = packageInfo.versionCode;
72 | versionName = packageInfo.versionName;
73 | } catch (PackageManager.NameNotFoundException ignored) { }
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/utils/AndroidUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.utils;
27 |
28 | import android.annotation.SuppressLint;
29 | import android.app.Activity;
30 | import android.app.ProgressDialog;
31 | import android.content.ClipData;
32 | import android.content.ClipboardManager;
33 | import android.content.Context;
34 | import android.content.Intent;
35 | import android.content.SharedPreferences;
36 | import android.net.Uri;
37 | import android.text.Editable;
38 | import android.text.TextWatcher;
39 | import android.widget.Toast;
40 |
41 | import androidx.preference.PreferenceManager;
42 |
43 | import com.fasterxml.jackson.databind.DeserializationFeature;
44 | import com.fasterxml.jackson.databind.ObjectMapper;
45 |
46 | import org.geysermc.geyser.android.MainActivity;
47 | import org.geysermc.geyser.android.R;
48 |
49 | import java.io.BufferedReader;
50 | import java.io.File;
51 | import java.io.FileReader;
52 | import java.io.IOException;
53 | import java.nio.file.Path;
54 | import java.nio.file.Paths;
55 |
56 | public class AndroidUtils {
57 |
58 | public static final ObjectMapper OBJECT_MAPPER = new ObjectMapper().disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
59 |
60 | private static ProgressDialog appLoader;
61 |
62 | /**
63 | * Open the default browser at a given URL
64 | *
65 | * @param url The URL to show
66 | */
67 | public static void showURL(String url) {
68 | Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
69 | MainActivity.getContext().startActivity(browserIntent);
70 | }
71 |
72 |
73 | /**
74 | * Show a toast message with Toast.LENGTH_SHORT
75 | *
76 | * @param ctx The app context
77 | * @param message The message to show
78 | */
79 | public static void showToast(Context ctx, String message) {
80 | showToast(ctx, message, Toast.LENGTH_SHORT);
81 | }
82 |
83 | /**
84 | * Show a toast message with the given length
85 | *
86 | * @param ctx The app context
87 | * @param message The message to show
88 | * @param length The length to show the toast for
89 | */
90 | public static void showToast(Context ctx, String message, int length) {
91 | Toast toast = Toast.makeText(ctx, message, length);
92 | toast.show();
93 | }
94 |
95 | /**
96 | * Read the given file as a string
97 | *
98 | * @param file The file to read
99 | * @return The string contents of the file
100 | */
101 | public static String fileToString(File file) {
102 | try {
103 | BufferedReader reader = new BufferedReader(new FileReader(file));
104 | StringBuilder stringBuilder = new StringBuilder();
105 | String line;
106 | String ls = System.getProperty("line.separator");
107 | while ((line = reader.readLine()) != null) {
108 | stringBuilder.append(line);
109 | stringBuilder.append(ls);
110 | }
111 |
112 | stringBuilder.deleteCharAt(stringBuilder.length() - 1);
113 | reader.close();
114 |
115 | return stringBuilder.toString();
116 | } catch (IOException e) {
117 | return "";
118 | }
119 | }
120 |
121 | /**
122 | * Remove all Minecraft color codes from a string
123 | *
124 | * @param line The string to use
125 | * @return The sanitised string
126 | */
127 | public static CharSequence purgeColorCodes(String line) {
128 | return line.replaceAll("\u00A7[0-9a-fA-F]", "");
129 | }
130 |
131 | /**
132 | * Get the storage path based on the config
133 | *
134 | * @param ctx The app context
135 | * @return The path of the chosen storage location
136 | */
137 | @SuppressLint("NewApi")
138 | public static Path getStoragePath(Context ctx) {
139 | File storageDir = ctx.getFilesDir();
140 |
141 | // Get the current storage preference and change the path accordingly
142 | SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
143 | if ("external".equals(preferences.getString("geyser_storage", "internal"))) {
144 | storageDir = ctx.getExternalFilesDir("");
145 | }
146 |
147 | assert storageDir != null;
148 | return Paths.get(storageDir.getPath());
149 | }
150 |
151 | /**
152 | * Create a popup loader for the current context
153 | *
154 | * @param ctx Context to create the loader for
155 | */
156 | public static void ShowLoader(Context ctx) {
157 | if (appLoader != null && appLoader.isShowing()) {
158 | appLoader.hide();
159 | }
160 |
161 | appLoader = new ProgressDialog(ctx);
162 | appLoader.setTitle(ctx.getString(R.string.utils_loader));
163 | appLoader.setIndeterminate(false);
164 | appLoader.setCancelable(true);
165 |
166 | appLoader.show();
167 | }
168 |
169 | /**
170 | * Hide the loader if there is one
171 | */
172 | public static void HideLoader() {
173 | if (appLoader != null) {
174 | appLoader.dismiss();
175 | }
176 | }
177 |
178 | /**
179 | * Run an action on the UI thread.
180 | * Checks if the activity is not null first.
181 | *
182 | * @param activity Activity to run the action on
183 | * @param action The action to run
184 | */
185 | public static void runOnUiThread(Activity activity, Runnable action) {
186 | if (activity != null) {
187 | activity.runOnUiThread(action);
188 | }
189 | }
190 |
191 | /**
192 | * Set the clipboard text
193 | *
194 | * @param context Context to use
195 | * @param text Text to store
196 | */
197 | public static void setClipboard(Context context, String text) {
198 | ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
199 | ClipData clip = ClipData.newPlainText(context.getResources().getString(R.string.utils_copy_success), text);
200 | clipboard.setPrimaryClip(clip);
201 | }
202 |
203 | /**
204 | * Generate a {@link TextWatcher} that calls a {@link org.geysermc.geyser.android.utils.EventListeners.AfterTextChangeListener} after a text change
205 | *
206 | * @param listener The listener to call
207 | * @return The built {@link TextWatcher}
208 | */
209 | public static TextWatcher generateAfterTextChange(EventListeners.AfterTextChangeListener listener) {
210 | return new TextWatcher() {
211 | @Override
212 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
213 |
214 | @Override
215 | public void onTextChanged(CharSequence s, int start, int before, int count) { }
216 |
217 | @Override
218 | public void afterTextChanged(Editable s) {
219 | if (listener != null) listener.afterTextChange(s);
220 | }
221 | };
222 | }
223 | }
224 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/utils/ConfigUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.utils;
27 |
28 | import com.fasterxml.jackson.databind.BeanDescription;
29 | import com.fasterxml.jackson.databind.JavaType;
30 | import com.fasterxml.jackson.databind.ObjectMapper;
31 | import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
32 |
33 | import java.util.List;
34 | import java.util.Set;
35 | import java.util.stream.Collectors;
36 |
37 | public class ConfigUtils {
38 |
39 | private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
40 |
41 | /**
42 | * Get the {@link BeanPropertyDefinition}s for the given class
43 | *
44 | * @param clazz The class to get the definitions for
45 | * @return A list of {@link BeanPropertyDefinition} for the given class
46 | */
47 | public static List getPOJOForClass(Class> clazz) {
48 | JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructType(clazz);
49 |
50 | // Introspect the given type
51 | BeanDescription beanDescription = OBJECT_MAPPER.getSerializationConfig().introspect(javaType);
52 |
53 | // Find properties
54 | List properties = beanDescription.findProperties();
55 |
56 | // Get the ignored properties
57 | Set ignoredProperties = OBJECT_MAPPER.getSerializationConfig().getAnnotationIntrospector()
58 | .findPropertyIgnorals(beanDescription.getClassInfo()).getIgnored();
59 |
60 | // Filter properties removing the ignored ones
61 | return properties.stream()
62 | .filter(property -> !ignoredProperties.contains(property.getName()))
63 | .collect(Collectors.toList());
64 | }
65 |
66 | /**
67 | * Get the value of a {@link BeanPropertyDefinition} forces if not directly accessible
68 | *
69 | * @param property The property to get
70 | * @param parentObject The parent to get the property from
71 | * @return The value of the property
72 | */
73 | public static Object forceGet(BeanPropertyDefinition property, Object parentObject) {
74 | try {
75 | // Try get it normally
76 | return property.getGetter().callOn(parentObject);
77 | } catch (NullPointerException e) {
78 | // Force the get
79 | property.getField().fixAccess(true);
80 | return property.getField().getValue(parentObject);
81 | } catch (Exception ignored) { }
82 |
83 | return null;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/utils/EventListeners.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.utils;
27 |
28 | import android.text.Editable;
29 |
30 | /**
31 | * This class is used to store various interfaces for event listeners
32 | */
33 | public class EventListeners {
34 |
35 | /**
36 | * This is used for adding a listener to the onDisable method of the {@link org.geysermc.geyser.android.geyser.GeyserAndroidBootstrap}
37 | */
38 | public interface OnDisableEventListener {
39 | void onDisable();
40 | }
41 |
42 | /**
43 | * This is used for adding a listener to the log events in both
44 | * {@link org.geysermc.geyser.android.proxy.ProxyLogger} and {@link org.geysermc.geyser.android.geyser.GeyserAndroidLogger}
45 | */
46 | public interface LogEventListener {
47 | void onLogLine(String line);
48 | }
49 |
50 | /**
51 | * This is used for when the background service has finished starting
52 | */
53 | public interface StartedEventListener {
54 | void onStarted(boolean failed);
55 | }
56 |
57 | /**
58 | * This is used for running a function after the text has changed in a {@link android.text.TextWatcher}
59 | */
60 | public interface AfterTextChangeListener {
61 | void afterTextChange(Editable editable);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/app/src/main/java/org/geysermc/geyser/android/utils/UserAuth.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (c) 2020-2020 GeyserMC. http://geysermc.org
3 | *
4 | * Permission is hereby granted, free of charge, to any person obtaining a copy
5 | * of this software and associated documentation files (the "Software"), to deal
6 | * in the Software without restriction, including without limitation the rights
7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8 | * copies of the Software, and to permit persons to whom the Software is
9 | * furnished to do so, subject to the following conditions:
10 | *
11 | * The above copyright notice and this permission notice shall be included in
12 | * all copies or substantial portions of the Software.
13 | *
14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20 | * THE SOFTWARE.
21 | *
22 | * @author GeyserMC
23 | * @link https://github.com/GeyserMC/GeyserAndroid
24 | */
25 |
26 | package org.geysermc.geyser.android.utils;
27 |
28 | import com.fasterxml.jackson.annotation.JsonProperty;
29 |
30 | import lombok.AllArgsConstructor;
31 | import lombok.Getter;
32 | import lombok.NoArgsConstructor;
33 | import lombok.Setter;
34 |
35 | @Getter
36 | @Setter
37 | @AllArgsConstructor
38 | @NoArgsConstructor
39 | public class UserAuth {
40 | private String email;
41 | private String password;
42 | @JsonProperty("microsoft-account")
43 | private boolean microsoftAccount = false;
44 | }
45 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/geyser_logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/app/src/main/res/drawable/geyser_logo.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_add.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_discord.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_home.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_proxy.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_menu_website.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_notification_logs.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/nav_view_item_iconcolor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/nav_view_item_shapecolor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/nav_view_item_textcolor.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/side_nav_bar.xml:
--------------------------------------------------------------------------------
1 |
3 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_about.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_config_editor_advanced_pretty.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_config_editor_advanced_raw.xml:
--------------------------------------------------------------------------------
1 |
5 |
6 |
23 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_config_editor_simple.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
18 |
19 |
33 |
34 |
43 |
44 |
58 |
59 |
68 |
69 |
80 |
81 |
93 |
94 |
106 |
107 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
15 |
16 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_settings.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_user_auths.xml:
--------------------------------------------------------------------------------
1 |
4 |
5 |
9 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/app_bar_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
13 |
14 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/content_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/dialog_user_auth.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
15 |
16 |
25 |
26 |
35 |
36 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_home.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
22 |
23 |
35 |
36 |
47 |
48 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/fragment_proxy.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
21 |
22 |
31 |
32 |
47 |
48 |
60 |
61 |
74 |
75 |
84 |
85 |
107 |
108 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/nav_header_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
14 |
15 |
21 |
22 |
28 |
29 |
34 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/activity_main_drawer.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/navigation/mobile_navigation.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
13 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/app/src/main/res/values/arrays.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | - Internal
5 | - External
6 |
7 |
8 |
9 | - internal
10 | - external
11 |
12 |
13 |
14 |
15 | - Pretty
16 | - Raw
17 |
18 |
19 |
20 | - pretty
21 | - raw
22 |
23 |
24 |
25 |
26 | - Online
27 | - Offline
28 |
29 |
30 |
31 |
32 | - Dark
33 | - Light
34 | - System
35 |
36 |
37 |
38 | - dark
39 | - light
40 | - system
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #608ed2
4 | #2B5797
5 | #1C3963
6 | #029788
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 16dp
4 | 16dp
5 | 8dp
6 | 176dp
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/drawables.xml:
--------------------------------------------------------------------------------
1 |
2 | - @android:drawable/ic_menu_manage
3 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Geyser Android
3 | https://geysermc.org
4 | https://discord.gg/geysermc
5 |
6 | Geyser Android
7 | geysermc.org
8 |
9 | test.geysermc.org
10 | 19132
11 | 25565
12 |
13 | ]]>
14 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/about_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
12 |
13 |
14 |
18 |
19 |
23 |
24 |
28 |
29 |
30 |
31 |
32 |
33 |
37 |
38 |
42 |
43 |
47 |
48 |
49 |
50 |
51 |
52 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/root_preferences.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
12 |
13 |
20 |
21 |
24 |
25 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | google()
5 | jcenter()
6 |
7 | maven {
8 | url 'https://jitpack.io'
9 | }
10 | }
11 | dependencies {
12 | classpath 'com.android.tools.build:gradle:4.0.2'
13 |
14 | // NOTE: Do not place your application dependencies here; they belong
15 | // in the individual module build.gradle files
16 |
17 | classpath "com.github.shevek:jarjar:9a7eca72f9"
18 | classpath "com.github.jengelman.gradle.plugins:shadow:6.0.0"
19 | }
20 | }
21 |
22 | allprojects {
23 | repositories {
24 | google()
25 | jcenter()
26 | }
27 | }
28 |
29 | task clean(type: Delete) {
30 | delete rootProject.buildDir
31 | }
--------------------------------------------------------------------------------
/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=-Xmx2048m
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
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Jul 04 17:00:00 BST 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-6.1.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 |
--------------------------------------------------------------------------------
/metadata/full_description.txt:
--------------------------------------------------------------------------------
1 | Geyser is a proxy, bridging the gap between Minecraft: Bedrock Edition and
2 | Minecraft: Java Edition servers. The ultimate goal of this project is to allow
3 | Minecraft: Bedrock Edition users to join Minecraft: Java Edition servers as
4 | seamlessly as possible. Please note, this project is still a work in progress
5 | and should not be used on production. Expect bugs!
6 |
7 | This app requires an existing copy of Minecraft: Bedrock Edition on any
8 | platform. You need a paid copy of Minecraft: Java Edition to join online mode
9 | Java servers.
--------------------------------------------------------------------------------
/metadata/images/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/icon.png
--------------------------------------------------------------------------------
/metadata/images/phoneScreenshots/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/phoneScreenshots/1.jpg
--------------------------------------------------------------------------------
/metadata/images/phoneScreenshots/2.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/phoneScreenshots/2.jpg
--------------------------------------------------------------------------------
/metadata/images/phoneScreenshots/3.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/phoneScreenshots/3.jpg
--------------------------------------------------------------------------------
/metadata/images/phoneScreenshots/4.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/phoneScreenshots/4.jpg
--------------------------------------------------------------------------------
/metadata/images/phoneScreenshots/5.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/phoneScreenshots/5.jpg
--------------------------------------------------------------------------------
/metadata/images/phoneScreenshots/6.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/phoneScreenshots/6.jpg
--------------------------------------------------------------------------------
/metadata/images/phoneScreenshots/7.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/GeyserMC/GeyserAndroid/ef9167efd3e77a4a15bdc4b1ea403d1b0dd69179/metadata/images/phoneScreenshots/7.jpg
--------------------------------------------------------------------------------
/metadata/short_description.txt:
--------------------------------------------------------------------------------
1 | Connect to Minecraft: Java Edition servers with Minecraft: Bedrock edition.
2 |
--------------------------------------------------------------------------------
/metadata/title.txt:
--------------------------------------------------------------------------------
1 | Geyser
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 | rootProject.name = "GeyserAndroid"
--------------------------------------------------------------------------------