mirror of
https://github.com/2009scape/2009Scape-mobile.git
synced 2026-08-01 14:19:12 -06:00
Merge branch 'v3_openjdk' into forge-1.17-wip
This commit is contained in:
commit
3888ddf2e1
252 changed files with 9212 additions and 9758 deletions
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
2
.github/ISSUE_TEMPLATE/bug_report.yml
vendored
|
|
@ -22,7 +22,7 @@ body:
|
|||
|
||||
Tip: You can attach files by clicking this area to highlight it and then dragging files in or select them on 🖼 option at the toolbar.
|
||||
validations:
|
||||
required: false
|
||||
required: true
|
||||
|
||||
- type: textarea
|
||||
attributes:
|
||||
|
|
|
|||
91
.github/workflows/android.yml
vendored
91
.github/workflows/android.yml
vendored
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
|
||||
- name: Get gl4es latest commit hash
|
||||
id: gl4es-sha
|
||||
run: echo "::set-output name=sha::$(echo $(git ls-remote https://github.com/PojavLauncherTeam/gl4es refs/heads/master | grep -io '^\S*' && git ls-remote https://github.com/ptitSeb/gl4es refs/heads/master | grep -io '^\S*'))"
|
||||
run: echo "::set-output name=sha::$(echo $(git ls-remote https://github.com/PojavLauncherTeam/gl4es-114-extra refs/heads/master | grep -io '^\S*'))"
|
||||
shell: bash
|
||||
|
||||
- name: Cache gl4es
|
||||
|
|
@ -34,13 +34,13 @@ jobs:
|
|||
id: gl4es-cache
|
||||
with:
|
||||
path: gl4es/libs
|
||||
key: gl4es-android-shared-nodbg-test1-2-${{ steps.gl4es-sha.outputs.sha }}
|
||||
key: gl4es-holy-android-shared-${{ steps.gl4es-sha.outputs.sha }}
|
||||
|
||||
- name: Get gl4es
|
||||
if: steps.gl4es-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'ptitSeb/gl4es'
|
||||
repository: 'PojavLauncherTeam/gl4es-114-extra'
|
||||
path: 'gl4es'
|
||||
|
||||
- name: Build gl4es
|
||||
|
|
@ -50,56 +50,15 @@ jobs:
|
|||
cd gl4es
|
||||
git config --global user.email "github-actions@users.noreply.github.com"
|
||||
git config --global user.name "github-actions"
|
||||
git remote add upstream https://github.com/PojavLauncherTeam/gl4es
|
||||
git fetch upstream
|
||||
git checkout master
|
||||
git merge --allow-unrelated-histories upstream/master || echo "Merge exit code $?"
|
||||
$ANDROID_NDK_HOME/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
|
||||
cp -R libs/* ../app_pojavlauncher/src/main/jniLibs/
|
||||
|
||||
- name: Install gl4es
|
||||
if: steps.gl4es-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -R gl4es/libs/* app_pojavlauncher/src/main/jniLibs/
|
||||
mv gl4es ..
|
||||
git add .
|
||||
git commit -am "CI: Update gl4es"
|
||||
git push
|
||||
|
||||
- name: Get vgpu latest commit hash
|
||||
id: vgpu-sha
|
||||
run: echo "::set-output name=sha::$(echo $(git ls-remote https://github.com/PojavLauncherTeam/VGPU refs/heads/main | grep -io '^\S*'))"
|
||||
shell: bash
|
||||
|
||||
- name: Cache vgpu
|
||||
uses: actions/cache@v2
|
||||
id: vgpu-cache
|
||||
with:
|
||||
path: vgpu/libs
|
||||
key: vgpu-android-shared-1-${{ steps.vgpu-sha.outputs.sha }}
|
||||
|
||||
- name: Get vgpu
|
||||
if: steps.vgpu-cache.outputs.cache-hit != 'true'
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
repository: 'PojavLauncherTeam/VGPU'
|
||||
path: 'vgpu'
|
||||
|
||||
- name: Build vgpu
|
||||
if: steps.vgpu-cache.outputs.cache-hit != 'true'
|
||||
- name: Push gl4es
|
||||
if: github.event != 'pull_request' && steps.gl4es-cache.outputs.cache-hit != 'true'
|
||||
continue-on-error: true
|
||||
run: |
|
||||
cd vgpu
|
||||
$ANDROID_NDK_HOME/ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk
|
||||
|
||||
- name: Install vgpu
|
||||
if: steps.vgpu-cache.outputs.cache-hit != 'true'
|
||||
run: |
|
||||
cp -R vgpu/libs/* app_pojavlauncher/src/main/jniLibs/
|
||||
mv vgpu ..
|
||||
git config --global user.email "github-actions@users.noreply.github.com"
|
||||
git config --global user.name "github-actions"
|
||||
git add .
|
||||
git commit -am "CI: Update vgpu"
|
||||
git commit -am "CI: Update gl4es"
|
||||
git push
|
||||
|
||||
- name: Get JRE8
|
||||
|
|
@ -112,9 +71,11 @@ jobs:
|
|||
repo: PojavLauncherTeam/android-openjdk-build-multiarch
|
||||
branch: buildjre8
|
||||
name: jre8-pojav
|
||||
run_id: 2140559381
|
||||
|
||||
- name: Build APK with Gradle
|
||||
- name: Build JRE JAR files
|
||||
run: |
|
||||
cp -R gl4es/libs/* app_pojavlauncher/src/main/jniLibs/
|
||||
chmod +x scripts/languagelist_updater.sh
|
||||
bash scripts/languagelist_updater.sh
|
||||
|
||||
|
|
@ -123,35 +84,41 @@ jobs:
|
|||
./gradlew clean
|
||||
# Build JRE JAR files (security manager, etc...)
|
||||
./gradlew :jre_lwjgl3glfw:build
|
||||
# mkdir app_pojavlauncher/src/main/assets/components/internal_libs
|
||||
rm app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar
|
||||
cp jre_lwjgl3glfw/build/libs/jre_lwjgl3glfw-3.2.3.jar app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar
|
||||
|
||||
- name: Build Google Play .aab
|
||||
if: github.repository_owner == 'PojavLauncherTeam' && github.ref_name == 'v3_openjdk'
|
||||
run: |
|
||||
./gradlew :app_pojavlauncher:bundleGplay
|
||||
mv app_pojavlauncher/build/outputs/bundle/gplay/app_pojavlauncher-gplay.aab out/app-gplay.aab
|
||||
|
||||
|
||||
- name: Build Debug .apk
|
||||
run: |
|
||||
# Build the launcher
|
||||
./gradlew :app_pojavlauncher:assembleDebug
|
||||
mv app_pojavlauncher/build/outputs/apk/debug/app_pojavlauncher-debug.apk out/app-debug.apk
|
||||
# mv app_pojavlauncher/build/intermediates/merged_native_libs/debug/out/lib out/debug_lib
|
||||
|
||||
- name: Build APK without runtime
|
||||
run: |
|
||||
rm -r app_pojavlauncher/src/main/assets/components/jre
|
||||
./gradlew assembleDebug
|
||||
mv app_pojavlauncher/build/outputs/apk/debug/app_pojavlauncher-debug.apk out/app-debug-noruntime.apk
|
||||
|
||||
- name: Upload APK
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: app-debug
|
||||
path: out/app-debug.apk
|
||||
# - name: Upload onto the Google Play testing track
|
||||
# uses: r0adkll/upload-google-play@v1.0.15
|
||||
# with:
|
||||
# serviceAccountJsonPlainText: ${{ secrets.GPLAY_SERVICE_JSON }}
|
||||
# packageName: net.kdt.pojavlaunch
|
||||
# releaseFiles: out/app-gplay.aab
|
||||
# track: internal
|
||||
# inAppUpdatePriority: 5
|
||||
|
||||
- name: Upload onto the Google Play testing track
|
||||
if: github.repository_owner == 'PojavLauncherTeam' && github.ref_name == 'v3_openjdk'
|
||||
uses: r0adkll/upload-google-play@v1.0.15
|
||||
with:
|
||||
serviceAccountJsonPlainText: ${{ secrets.GPLAY_SERVICE_JSON }}
|
||||
packageName: net.kdt.pojavlaunch
|
||||
releaseFiles: out/app-gplay.aab
|
||||
track: internal
|
||||
inAppUpdatePriority: 5
|
||||
|
||||
- name: Upload APK (without runtime)
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
|
|
|
|||
32
README.md
32
README.md
|
|
@ -2,7 +2,7 @@
|
|||
[](https://crowdin.com/project/pojavlauncher)
|
||||
[](https://discord.gg/6RpEJda)
|
||||
[](https://reddit.com/r/PojavLauncher)
|
||||
[](https://play.google.com/store/apps/details?id=net.kdt.pojavlaunch)
|
||||
[](https://play.google.com/store/apps/details?id=net.kdt.pojavlaunch)
|
||||
|
||||
# PojavLauncher
|
||||
|
||||
|
|
@ -10,16 +10,16 @@
|
|||
- We do not exist on TikTok. No one from the dev team makes TikTok videos.
|
||||
- The official Twitter for PojavLauncher is [@PLaunchTeam](https://twitter.com/PLaunchTeam). Any others (most notably @PojavLauncher) are fake, please report them to Twitter's moderation team.
|
||||
|
||||
## Navigation
|
||||
## Navigation
|
||||
- [Introduction](#introduction)
|
||||
- [Building](#building)
|
||||
- [Current status](#current-status)
|
||||
- [License](#license)
|
||||
- [License](#license)
|
||||
- [Contributing](#contributing)
|
||||
- [Credits & Third party components and their licenses](#credits--third-party-components-and-their-licenses)
|
||||
|
||||
## Introduction
|
||||
PojavLauncher is a Minecraft: Java Edition launcher for Android and iOS based on [Boardwalk](https://github.com/zhuowei/Boardwalk). This launcher can launch almost all available Minecraft versions (from rd-132211 to 21w08b (1.17) snapshot, including Combat Test versions). Modding via Forge and Fabric are also supported. This repository contains source code for Android. For iOS/iPadOS, check out [PojavLauncher_iOS](https://github.com/PojavLauncherTeam/PojavLauncher_iOS).
|
||||
PojavLauncher is a Minecraft: Java Edition launcher for Android and iOS based on [Boardwalk](https://github.com/zhuowei/Boardwalk). This launcher can launch almost all available Minecraft versions (from rd-132211 to 1.18 snapshots (kinda)), including Combat Test versions. Modding via Forge (up to 1.16.5) and Fabric are also supported. This repository contains source code for Android. For iOS/iPadOS, check out [PojavLauncher_iOS](https://github.com/PojavLauncherTeam/PojavLauncher_iOS).
|
||||
|
||||
## Building
|
||||
To get started, you can just get prebuilt app from [stable release](https://github.com/PojavLauncherTeam/PojavLauncher/releases) or [automatic builds](https://github.com/PojavLauncherTeam/PojavLauncher/actions). If you want to build after launcher code changes, follow steps below.
|
||||
|
|
@ -57,9 +57,6 @@ scripts\languagelist_updater.bat
|
|||
```
|
||||
# Build GLFW stub
|
||||
./gradlew :jre_lwjgl3glfw:build
|
||||
# mkdir app_pojavlauncher/src/main/assets/components/internal_libs
|
||||
rm app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar
|
||||
cp jre_lwjgl3glfw/build/libs/jre_lwjgl3glfw-3.2.3.jar app_pojavlauncher/src/main/assets/components/lwjgl3/lwjgl-glfw-classes.jar
|
||||
|
||||
# Build the launcher
|
||||
./gradlew :app_pojavlauncher:assembleDebug
|
||||
|
|
@ -80,13 +77,13 @@ cp jre_lwjgl3glfw/build/libs/jre_lwjgl3glfw-3.2.3.jar app_pojavlauncher/src/main
|
|||
- [ ] More...
|
||||
|
||||
## Known Issues
|
||||
- Minecraft `21w10a` or newer are currently not yet supported due to the new GLSL usage.
|
||||
- In 1.16 and up spawn eggs banners are white (you can fix this by switching renderer
|
||||
to `gl4es 1.1.5`, only works on 1.16 and up, do not use under this version)
|
||||
- Controller mods aren't working
|
||||
- Minecraft `21w10a` or newer are currently not yet supported due to the new GLSL usage. Fortunately, a workaround is provided and built into the launcher.
|
||||
- In 1.16 and up, spawn eggs banners are white (you can fix this by switching renderer
|
||||
to `gl4es 1.1.5`, only works on 1.16 and up, do not use under this version, as the texture
|
||||
will bug out when hit a mob)
|
||||
- Controller mods aren't working.
|
||||
- Random crashes could happen very often on Android 5.x during game load or join world.
|
||||
- With big modpacks textures could be messed up
|
||||
- If you're using gl4es 1.1.5 on 1.16 and lower texture will bug out when hit a mob
|
||||
- probably more, that's why we have a bug tracker ;)
|
||||
|
||||
## License
|
||||
|
|
@ -96,13 +93,16 @@ to `gl4es 1.1.5`, only works on 1.16 and up, do not use under this version)
|
|||
Contributions are welcome! We welcome any type of contribution, not only code.
|
||||
Any code change should be submitted as a pull request. The description should explain what the code does and give steps to execute it.
|
||||
|
||||
## Credits & Third party components and their licenses
|
||||
## Credits & Third party components and their licenses (if available)
|
||||
- [Boardwalk](https://github.com/zhuowei/Boardwalk) (JVM Launcher): Unknown License/[Apache License 2.0](https://github.com/zhuowei/Boardwalk/blob/master/LICENSE) or GNU GPLv2.
|
||||
- Android Support Libraries: [Apache License 2.0](https://android.googlesource.com/platform/prebuilts/maven_repo/android/+/master/NOTICE.txt).
|
||||
- [GL4ES](https://github.com/ptitSeb/gl4es): [MIT License](https://github.com/ptitSeb/gl4es/blob/master/LICENSE).<br>
|
||||
- [GL4ES](https://github.com/PojavLauncherTeam/gl4es): [MIT License](https://github.com/ptitSeb/gl4es/blob/master/LICENSE).<br>
|
||||
- [OpenJDK](https://github.com/PojavLauncherTeam/openjdk-multiarch-jdk8u): [GNU GPLv2 License](https://openjdk.java.net/legal/gplv2+ce.html).<br>
|
||||
- [LWJGL3](https://github.com/PojavLauncherTeam/lwjgl3): [BSD-3 License](https://github.com/LWJGL/lwjgl3/blob/master/LICENSE.md).
|
||||
- [LWJGLX](https://github.com/PojavLauncherTeam/lwjglx) (LWJGL2 API compatibility layer for LWJGL3): unknown license.<br>
|
||||
- [Mesa 3D Graphics Library](https://gitlab.freedesktop.org/mesa/mesa): [MIT License](https://docs.mesa3d.org/license.html).
|
||||
- [pro-grade](https://github.com/pro-grade/pro-grade) (Java sandboxing security manager): [Apache License 2.0](https://github.com/pro-grade/pro-grade/blob/master/LICENSE.txt).
|
||||
- [xHook](https://github.com/iqiyi/xHook) (Used for exit code trapping): [MIT and BSD-style licenses](https://github.com/iqiyi/xHook/blob/master/LICENSE)
|
||||
|
||||
- [xHook](https://github.com/iqiyi/xHook) (Used for exit code trapping): [MIT and BSD-style licenses](https://github.com/iqiyi/xHook/blob/master/LICENSE).
|
||||
- [libepoxy](https://github.com/anholt/libepoxy): [MIT License](https://github.com/anholt/libepoxy/blob/master/COPYING).
|
||||
- [virglrenderer](https://github.com/PojavLauncherTeam/virglrenderer): [MIT License](https://gitlab.freedesktop.org/virgl/virglrenderer/-/blob/master/COPYING).
|
||||
- Thanks to [MCHeads](https://mc-heads.net) for providing Minecraft avatars.
|
||||
|
|
|
|||
|
|
@ -1,16 +1,16 @@
|
|||
apply plugin: 'com.android.application'
|
||||
|
||||
def getDate() { return new Date().format('yyyyMMdd')}
|
||||
static def getDate() { return new Date().format('yyyyMMdd')}
|
||||
|
||||
def getDateSeconds() {
|
||||
if(System.getenv("GITHUB_ACTIONS").equals("true")) {
|
||||
static def getDateSeconds() {
|
||||
if(System.getenv("GITHUB_ACTIONS") == "true") {
|
||||
return 9934841+Integer.parseInt(System.getenv("GITHUB_RUN_NUMBER"))
|
||||
}else{
|
||||
return 172005
|
||||
}
|
||||
}
|
||||
|
||||
def getVersionName = {
|
||||
def getVersionName = {
|
||||
// Get the last version tag, as well as the short head of the last commit
|
||||
ByteArrayOutputStream TAG = new ByteArrayOutputStream()
|
||||
ByteArrayOutputStream BRANCH = new ByteArrayOutputStream()
|
||||
|
|
@ -37,7 +37,7 @@ def getVersionName = {
|
|||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
compileSdkVersion 31
|
||||
dexOptions {
|
||||
javaMaxHeapSize "4g"
|
||||
}
|
||||
|
|
@ -74,10 +74,16 @@ android {
|
|||
debug {
|
||||
applicationIdSuffix '.debug'
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
shrinkResources false
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
signingConfig signingConfigs.customDebug
|
||||
resValue 'string', 'storageProviderAuthorities', 'net.kdt.pojavlaunch.scoped.gamefolder.debug'
|
||||
}
|
||||
proguard {
|
||||
initWith debug
|
||||
minifyEnabled true
|
||||
shrinkResources true
|
||||
}
|
||||
|
||||
release {
|
||||
// Don't set to true or java.awt will be a.a or something similar.
|
||||
|
|
@ -116,17 +122,14 @@ dependencies {
|
|||
implementation 'javax.annotation:javax.annotation-api:1.3.2'
|
||||
implementation 'commons-codec:commons-codec:1.14'
|
||||
// implementation 'com.wu-man:android-bsf-api:3.1.3'
|
||||
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
|
||||
implementation 'androidx.preference:preference:1.1.1'
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'androidx.legacy:legacy-preference-v14:1.0.0'
|
||||
implementation 'com.google.android.material:material:1.4.0'
|
||||
implementation 'androidx.annotation:annotation:1.2.0'
|
||||
implementation 'androidx.browser:browser:1.3.0'
|
||||
//implementation 'androidx.core:core:1.7.0'
|
||||
implementation 'androidx.drawerlayout:drawerlayout:1.1.1'
|
||||
implementation 'androidx.viewpager2:viewpager2:1.0.0'
|
||||
implementation 'androidx.annotation:annotation:1.3.0'
|
||||
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.0.4"
|
||||
implementation "androidx.constraintlayout:constraintlayout:2.1.2"
|
||||
|
||||
implementation 'com.rarepebble:colorpicker:3.0.1'
|
||||
implementation 'com.github.duanhong169:checkerboarddrawable:1.0.2'
|
||||
|
||||
|
||||
|
|
@ -134,8 +137,7 @@ dependencies {
|
|||
// implementation 'com.intuit.ssp:ssp-android:1.0.5'
|
||||
|
||||
implementation 'org.tukaani:xz:1.8'
|
||||
|
||||
implementation 'net.objecthunter:exp4j:0.4.8'
|
||||
implementation 'com.github.PojavLauncherTeam:exp4j:master-SNAPSHOT'
|
||||
// implementation 'net.sourceforge.streamsupport:streamsupport-cfuture:1.7.0'
|
||||
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||
}
|
||||
|
|
|
|||
11
app_pojavlauncher/proguard-rules.pro
vendored
11
app_pojavlauncher/proguard-rules.pro
vendored
|
|
@ -15,4 +15,13 @@
|
|||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
-keep class !android.support.v7.internal.view.menu.**,android.support.** {*;}
|
||||
|
||||
# We use Reflection on the builder to avoid creating too many objects
|
||||
-keep class net.objecthunter.exp4j.ExpressionBuilder**
|
||||
-keepclassmembers class net.objecthunter.exp4j.ExpressionBuilder** {
|
||||
*;
|
||||
}
|
||||
# Option screens
|
||||
-keep class net.kdt.pojavlaunch.prefs.screens** {*;}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
android:maxSdkVersion="28"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
|
|
@ -59,12 +60,12 @@
|
|||
</activity>
|
||||
|
||||
<activity
|
||||
android:theme="@style/MenuDialog"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".FatalErrorActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"/>
|
||||
<activity
|
||||
android:theme="@style/MenuDialog"
|
||||
android:theme="@style/Theme.AppCompat.DayNight.Dialog"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".ExitActivity"
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"/>
|
||||
|
|
@ -90,8 +91,7 @@
|
|||
android:multiprocess="true"
|
||||
android:screenOrientation="sensorLandscape"
|
||||
android:name=".MainActivity"
|
||||
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|keyboard|navigation"/>
|
||||
android:configChanges="keyboardHidden|orientation|screenSize|smallestScreenSize|screenLayout|keyboard|navigation"/>
|
||||
<provider
|
||||
android:name=".scoped.GameFolderProvider"
|
||||
android:authorities="@string/storageProviderAuthorities"
|
||||
|
|
|
|||
BIN
app_pojavlauncher/src/main/assets/arc_dns_injector.jar
Normal file
BIN
app_pojavlauncher/src/main/assets/arc_dns_injector.jar
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
20210430
|
||||
HEllo People
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -1 +1 @@
|
|||
20210827
|
||||
1652063179136
|
||||
File diff suppressed because it is too large
Load diff
|
|
@ -5,15 +5,15 @@ import android.graphics.Rect;
|
|||
import android.util.AttributeSet;
|
||||
import android.widget.ScrollView;
|
||||
|
||||
/**
|
||||
Class allowing to ignore the focusing from an item such an EditText within it.
|
||||
Ignoring it will stop the scrollView from refocusing on the view
|
||||
*/
|
||||
public class DefocusableScrollView extends ScrollView {
|
||||
|
||||
/*
|
||||
What is this class for ?
|
||||
It allows to ignore the focusing from an item such an EditText.
|
||||
Ignoring it will stop the scrollView from refocusing on the view
|
||||
*/
|
||||
|
||||
private boolean keepFocusing = false;
|
||||
|
||||
private boolean mKeepFocusing = false;
|
||||
|
||||
|
||||
public DefocusableScrollView(Context context) {
|
||||
|
|
@ -33,16 +33,16 @@ public class DefocusableScrollView extends ScrollView {
|
|||
}
|
||||
|
||||
public void setKeepFocusing(boolean shouldKeepFocusing){
|
||||
keepFocusing = shouldKeepFocusing;
|
||||
mKeepFocusing = shouldKeepFocusing;
|
||||
}
|
||||
|
||||
public boolean isKeepFocusing(){
|
||||
return keepFocusing;
|
||||
return mKeepFocusing;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int computeScrollDeltaToGetChildRectOnScreen(Rect rect) {
|
||||
if(!keepFocusing) return 0;
|
||||
if(!mKeepFocusing) return 0;
|
||||
return super.computeScrollDeltaToGetChildRectOnScreen(rect);
|
||||
}
|
||||
|
||||
|
|
|
|||
86
app_pojavlauncher/src/main/java/com/kdt/LoggerView.java
Normal file
86
app_pojavlauncher/src/main/java/com/kdt/LoggerView.java
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
package com.kdt;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Typeface;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.ToggleButton;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
|
||||
import net.kdt.pojavlaunch.Logger;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
|
||||
/**
|
||||
* A class able to display logs to the user.
|
||||
* It has support for the Logger class
|
||||
*/
|
||||
public class LoggerView extends ConstraintLayout {
|
||||
private Logger.eventLogListener mLogListener;
|
||||
private ToggleButton mToggleButton;
|
||||
private ScrollView mScrollView;
|
||||
private TextView mLogTextView;
|
||||
|
||||
|
||||
public LoggerView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public LoggerView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibility(int visibility) {
|
||||
super.setVisibility(visibility);
|
||||
// Triggers the log view shown state by default when viewing it
|
||||
mToggleButton.setChecked(visibility == VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inflate the layout, and add component behaviors
|
||||
*/
|
||||
private void init(){
|
||||
inflate(getContext(), R.layout.view_logger, this);
|
||||
mLogTextView = findViewById(R.id.content_log_view);
|
||||
mLogTextView.setTypeface(Typeface.MONOSPACE);
|
||||
//TODO clamp the max text so it doesn't go oob
|
||||
mLogTextView.setMaxLines(Integer.MAX_VALUE);
|
||||
mLogTextView.setEllipsize(null);
|
||||
mLogTextView.setVisibility(GONE);
|
||||
|
||||
// Toggle log visibility
|
||||
mToggleButton = findViewById(R.id.content_log_toggle_log);
|
||||
mToggleButton.setOnCheckedChangeListener(
|
||||
(compoundButton, isChecked) -> {
|
||||
mLogTextView.setVisibility(isChecked ? VISIBLE : GONE);
|
||||
if(!isChecked) mLogTextView.setText("");
|
||||
});
|
||||
mToggleButton.setChecked(false);
|
||||
|
||||
// Remove the loggerView from the user View
|
||||
ImageButton cancelButton = findViewById(R.id.log_view_cancel);
|
||||
cancelButton.setOnClickListener(view -> LoggerView.this.setVisibility(GONE));
|
||||
|
||||
// Set the scroll view
|
||||
mScrollView = findViewById(R.id.content_log_scroll);
|
||||
|
||||
// Listen to logs
|
||||
mLogListener = text -> {
|
||||
if(mLogTextView.getVisibility() != VISIBLE) return;
|
||||
post(() -> {
|
||||
mLogTextView.append(text + '\n');
|
||||
mScrollView.fullScroll(View.FOCUS_DOWN);
|
||||
});
|
||||
|
||||
};
|
||||
Logger.getInstance().setLogListener(mLogListener);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -8,8 +8,7 @@ import androidx.core.content.res.ResourcesCompat;
|
|||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
|
||||
public class MineButton extends androidx.appcompat.widget.AppCompatButton
|
||||
{
|
||||
public class MineButton extends androidx.appcompat.widget.AppCompatButton {
|
||||
|
||||
public MineButton(Context ctx) {
|
||||
this(ctx, null);
|
||||
|
|
|
|||
|
|
@ -3,23 +3,20 @@ package com.kdt.mcgui;
|
|||
import android.content.*;
|
||||
import android.util.*;
|
||||
import android.graphics.*;
|
||||
import android.widget.EditText;
|
||||
|
||||
public class MineEditText extends com.google.android.material.textfield.TextInputEditText
|
||||
{
|
||||
public MineEditText(Context ctx)
|
||||
{
|
||||
public class MineEditText extends androidx.appcompat.widget.AppCompatEditText {
|
||||
public MineEditText(Context ctx) {
|
||||
super(ctx);
|
||||
init();
|
||||
}
|
||||
|
||||
public MineEditText(Context ctx, AttributeSet attrs)
|
||||
{
|
||||
public MineEditText(Context ctx, AttributeSet attrs) {
|
||||
super(ctx, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
public void init()
|
||||
{
|
||||
public void init() {
|
||||
setBackgroundColor(Color.parseColor("#131313"));
|
||||
setPadding(5, 5, 5, 5);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,61 +5,26 @@ import android.graphics.*;
|
|||
import android.text.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.utils.*;
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
public class AWTCanvasView extends TextureView implements TextureView.SurfaceTextureListener, Runnable {
|
||||
private int mScaleFactor;
|
||||
private int[] mScales;
|
||||
|
||||
private int mWidth, mHeight;
|
||||
private boolean mIsDestroyed = false;
|
||||
|
||||
private TextPaint fpsPaint;
|
||||
private boolean attached = false;
|
||||
|
||||
// Temporary count fps https://stackoverflow.com/a/13729241
|
||||
private LinkedList<Long> times = new LinkedList<Long>(){{add(System.nanoTime());}};
|
||||
public static final int AWT_CANVAS_WIDTH = 600;
|
||||
public static final int AWT_CANVAS_HEIGHT = 420;
|
||||
private final int MAX_SIZE = 100;
|
||||
private final double NANOS = 1000000000.0;
|
||||
|
||||
/** Calculates and returns frames per second */
|
||||
private double fps() {
|
||||
long lastTime = System.nanoTime();
|
||||
double difference = (lastTime - times.getFirst()) / NANOS;
|
||||
times.addLast(lastTime);
|
||||
int size = times.size();
|
||||
if (size > MAX_SIZE) {
|
||||
times.removeFirst();
|
||||
}
|
||||
return difference > 0 ? times.size() / difference : 0.0;
|
||||
}
|
||||
private int mWidth, mHeight;
|
||||
private boolean mIsDestroyed = false;
|
||||
private final TextPaint mFpsPaint;
|
||||
private boolean mAttached = false;
|
||||
private boolean mDrawing;
|
||||
|
||||
/** Computes the scale to better fit the screen */
|
||||
void initScaleFactors(){
|
||||
initScaleFactors(0);
|
||||
}
|
||||
|
||||
void initScaleFactors(int forcedScale){
|
||||
//Could be optimized
|
||||
if(forcedScale < 1) { //Auto scale
|
||||
int minDimension = Math.min(CallbackBridge.physicalHeight, CallbackBridge.physicalWidth);
|
||||
mScaleFactor = Math.max(((3 * minDimension) / 1080) - 1, 1);
|
||||
}else{
|
||||
mScaleFactor = forcedScale;
|
||||
}
|
||||
|
||||
int[] scales = new int[2]; //Left, Top
|
||||
|
||||
scales[0] = (CallbackBridge.physicalWidth/2);
|
||||
scales[0] -= scales[0]/mScaleFactor;
|
||||
|
||||
scales[1] = (CallbackBridge.physicalHeight/2);
|
||||
scales[1] -= scales[1]/mScaleFactor;
|
||||
|
||||
mScales = scales;
|
||||
}
|
||||
// Temporary count fps https://stackoverflow.com/a/13729241
|
||||
private final LinkedList<Long> mTimes = new LinkedList<Long>(){{add(System.nanoTime());}};
|
||||
|
||||
public AWTCanvasView(Context ctx) {
|
||||
this(ctx, null);
|
||||
|
|
@ -67,21 +32,23 @@ public class AWTCanvasView extends TextureView implements TextureView.SurfaceTex
|
|||
|
||||
public AWTCanvasView(Context ctx, AttributeSet attrs) {
|
||||
super(ctx, attrs);
|
||||
// setWillNotDraw(false);
|
||||
|
||||
fpsPaint = new TextPaint();
|
||||
fpsPaint.setColor(Color.WHITE);
|
||||
fpsPaint.setTextSize(20);
|
||||
|
||||
mFpsPaint = new TextPaint();
|
||||
mFpsPaint.setColor(Color.WHITE);
|
||||
mFpsPaint.setTextSize(20);
|
||||
|
||||
|
||||
setSurfaceTextureListener(this);
|
||||
initScaleFactors();
|
||||
|
||||
post(this::refreshSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(SurfaceTexture texture, int w, int h) {
|
||||
getSurfaceTexture().setDefaultBufferSize(AWT_CANVAS_WIDTH, AWT_CANVAS_HEIGHT);
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
|
||||
|
||||
mIsDestroyed = false;
|
||||
new Thread(this, "AndroidAWTRenderer").start();
|
||||
}
|
||||
|
|
@ -94,51 +61,71 @@ public class AWTCanvasView extends TextureView implements TextureView.SurfaceTex
|
|||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(SurfaceTexture texture, int w, int h) {
|
||||
getSurfaceTexture().setDefaultBufferSize(AWT_CANVAS_WIDTH, AWT_CANVAS_HEIGHT);
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(SurfaceTexture texture) {
|
||||
getSurfaceTexture().setDefaultBufferSize(AWT_CANVAS_WIDTH, AWT_CANVAS_HEIGHT);
|
||||
}
|
||||
|
||||
private boolean mDrawing;
|
||||
private Surface mSurface;
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Canvas canvas;
|
||||
mSurface = new Surface(getSurfaceTexture());
|
||||
Surface surface = new Surface(getSurfaceTexture());
|
||||
|
||||
try {
|
||||
while (!mIsDestroyed && mSurface.isValid()) {
|
||||
canvas = mSurface.lockCanvas(null);
|
||||
while (!mIsDestroyed && surface.isValid()) {
|
||||
canvas = surface.lockCanvas(null);
|
||||
canvas.drawRGB(0, 0, 0);
|
||||
|
||||
if (!attached) {
|
||||
attached = CallbackBridge.nativeAttachThreadToOther(true, BaseMainActivity.isInputStackCall);
|
||||
if (!mAttached) {
|
||||
mAttached = CallbackBridge.nativeAttachThreadToOther(true, BaseMainActivity.isInputStackCall);
|
||||
} else {
|
||||
int[] rgbArray = JREUtils.renderAWTScreenFrame(/* canvas, mWidth, mHeight */);
|
||||
mDrawing = rgbArray != null;
|
||||
if (rgbArray != null) {
|
||||
|
||||
canvas.save();
|
||||
canvas.scale(mScaleFactor, mScaleFactor);
|
||||
canvas.translate(-mScales[0],-mScales[1]);
|
||||
|
||||
|
||||
canvas.drawBitmap(rgbArray, 0, CallbackBridge.physicalWidth, 0, 0, CallbackBridge.physicalWidth, CallbackBridge.physicalHeight, true, null);
|
||||
canvas.drawBitmap(rgbArray, 0, AWT_CANVAS_WIDTH, 0, 0, AWT_CANVAS_WIDTH, AWT_CANVAS_HEIGHT, true, null);
|
||||
canvas.restore();
|
||||
|
||||
}
|
||||
rgbArray = null;
|
||||
// System.gc();
|
||||
}
|
||||
canvas.drawText("FPS: " + (Math.round(fps() * 10) / 10) + ", attached=" + attached + ", drawing=" + mDrawing, 50, 50, fpsPaint);
|
||||
|
||||
mSurface.unlockCanvasAndPost(canvas);
|
||||
canvas.drawText("FPS: " + (Math.round(fps() * 10) / 10) + ", attached=" + mAttached + ", drawing=" + mDrawing, 0, 20, mFpsPaint);
|
||||
surface.unlockCanvasAndPost(canvas);
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(getContext(), th);
|
||||
} catch (Throwable throwable) {
|
||||
Tools.showError(getContext(), throwable);
|
||||
}
|
||||
surface.release();
|
||||
}
|
||||
|
||||
/** Calculates and returns frames per second */
|
||||
private double fps() {
|
||||
long lastTime = System.nanoTime();
|
||||
double difference = (lastTime - mTimes.getFirst()) / NANOS;
|
||||
mTimes.addLast(lastTime);
|
||||
int size = mTimes.size();
|
||||
if (size > MAX_SIZE) {
|
||||
mTimes.removeFirst();
|
||||
}
|
||||
return difference > 0 ? mTimes.size() / difference : 0.0;
|
||||
}
|
||||
|
||||
/** Make the view fit the proper aspect ratio of the surface */
|
||||
private void refreshSize(){
|
||||
ViewGroup.LayoutParams layoutParams = getLayoutParams();
|
||||
|
||||
if(getHeight() < getWidth()){
|
||||
layoutParams.width = AWT_CANVAS_WIDTH * getHeight() / AWT_CANVAS_HEIGHT;
|
||||
}else{
|
||||
layoutParams.height = AWT_CANVAS_HEIGHT * getWidth() / AWT_CANVAS_WIDTH;
|
||||
}
|
||||
|
||||
setLayoutParams(layoutParams);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,10 @@ public class AWTInputBridge {
|
|||
// TODO: Android -> AWT keycode mapping
|
||||
nativeSendData(EVENT_TYPE_KEY, (int) keychar, keycode, 0, 0);
|
||||
}
|
||||
|
||||
public static void sendChar(char keychar){
|
||||
nativeSendData(EVENT_TYPE_CHAR, (int) keychar, 0, 0, 0);
|
||||
}
|
||||
|
||||
public static void sendMousePress(int awtButtons, boolean isDown) {
|
||||
nativeSendData(EVENT_TYPE_MOUSE_BUTTON, awtButtons, isDown ? 1 : 0, 0, 0);
|
||||
|
|
|
|||
|
|
@ -24,8 +24,7 @@ package net.kdt.pojavlaunch;
|
|||
* or visit www.oracle.com if you need additional information or have any
|
||||
* questions.
|
||||
*/
|
||||
public class AWTInputEvent
|
||||
{
|
||||
public class AWTInputEvent {
|
||||
// InputEvent
|
||||
/**
|
||||
* This flag indicates that the Shift key was down when the event
|
||||
|
|
|
|||
|
|
@ -7,8 +7,7 @@ import net.kdt.pojavlaunch.utils.*;
|
|||
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_IGNORE_NOTCH;
|
||||
|
||||
public class BaseActivity extends AppCompatActivity
|
||||
{
|
||||
public abstract class BaseActivity extends AppCompatActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -24,7 +23,7 @@ public class BaseActivity extends AppCompatActivity
|
|||
@Override
|
||||
public void startActivity(Intent i) {
|
||||
super.startActivity(i);
|
||||
new Throwable("StartActivity").printStackTrace();
|
||||
//new Throwable("StartActivity").printStackTrace();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,98 +1,75 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static net.kdt.pojavlaunch.Tools.getFileName;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.text.*;
|
||||
import android.text.method.*;
|
||||
import android.view.*;
|
||||
import android.webkit.MimeTypeMap;
|
||||
import android.widget.*;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.*;
|
||||
import com.kdt.pickafile.*;
|
||||
|
||||
import java.io.*;
|
||||
import net.kdt.pojavlaunch.fragments.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Map;
|
||||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTConfigDialog;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
|
||||
import net.kdt.pojavlaunch.prefs.*;
|
||||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
import net.kdt.pojavlaunch.tasks.*;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import net.kdt.pojavlaunch.value.*;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
public abstract class BaseLauncherActivity extends BaseActivity {
|
||||
public Button mPlayButton;
|
||||
public ConsoleFragment mConsoleView;
|
||||
public CrashFragment mCrashView;
|
||||
public ProgressBar mLaunchProgress;
|
||||
public Spinner mVersionSelector;
|
||||
public MultiRTConfigDialog mRuntimeConfigDialog;
|
||||
public TextView mLaunchTextStatus, mTextVersion;
|
||||
public TextView mLaunchTextStatus;
|
||||
|
||||
public JMinecraftVersionList mVersionList;
|
||||
public MinecraftDownloaderTask mTask;
|
||||
public MinecraftAccount mProfile;
|
||||
public String[] mAvailableVersions;
|
||||
//public String[] mAvailableVersions;
|
||||
|
||||
public boolean mIsAssetsProcessing = false;
|
||||
protected boolean canBack = false;
|
||||
|
||||
public abstract void statusIsLaunching(boolean isLaunching);
|
||||
|
||||
public void mcaccLogout(View view) {
|
||||
//PojavProfile.reset();
|
||||
finish();
|
||||
|
||||
/**
|
||||
* Used by the custom control button from the layout_main_v4
|
||||
* @param view The view triggering the function
|
||||
*/
|
||||
public void launchCustomControlsActivity(View view){
|
||||
startActivity(new Intent(BaseLauncherActivity.this, CustomControlsActivity.class));
|
||||
}
|
||||
|
||||
|
||||
public void launcherMenu(View view) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.mcl_options);
|
||||
builder.setItems(R.array.mcl_options, new DialogInterface.OnClickListener(){
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface p1, int p2)
|
||||
{
|
||||
switch (p2) {
|
||||
case 0: // Mod installer
|
||||
installMod(false);
|
||||
break;
|
||||
case 1: // Mod installer with java args
|
||||
installMod(true);
|
||||
break;
|
||||
case 2: // Custom controls
|
||||
startActivity(new Intent(BaseLauncherActivity.this, CustomControlsActivity.class));
|
||||
break;
|
||||
case 3: { // About
|
||||
final AlertDialog.Builder aboutB = new AlertDialog.Builder(BaseLauncherActivity.this);
|
||||
aboutB.setTitle(R.string.mcl_option_about);
|
||||
try {
|
||||
aboutB.setMessage(Html.fromHtml(String.format(Tools.read(getAssets().open("about_en.txt")),
|
||||
Tools.APP_NAME,
|
||||
BuildConfig.VERSION_NAME,
|
||||
"3.2.3")
|
||||
));
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
aboutB.setPositiveButton(android.R.string.ok, null);
|
||||
AlertDialog aboutDialog = aboutB.show();
|
||||
TextView aboutTv = aboutDialog.findViewById(android.R.id.message);
|
||||
aboutTv.setMovementMethod(LinkMovementMethod.getInstance());
|
||||
} break;
|
||||
}
|
||||
}
|
||||
});
|
||||
builder.show();
|
||||
/**
|
||||
* Used by the install button from the layout_main_v4
|
||||
* @param view The view triggering the function
|
||||
*/
|
||||
public void installJarFile(View view){
|
||||
installMod(false);
|
||||
}
|
||||
|
||||
|
||||
public static final int RUN_MOD_INSTALLER = 2050;
|
||||
private void installMod(boolean customJavaArgs) {
|
||||
if (MultiRTUtils.getExactJreName(8) == null) {
|
||||
Toast.makeText(this, R.string.multirt_nojava8rt, Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
if (customJavaArgs) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.alerttitle_installmod);
|
||||
|
|
@ -101,15 +78,12 @@ public abstract class BaseLauncherActivity extends BaseActivity {
|
|||
final EditText edit = new EditText(this);
|
||||
edit.setSingleLine();
|
||||
edit.setHint("-jar/-cp /path/to/file.jar ...");
|
||||
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
|
||||
@Override
|
||||
public void onClick(DialogInterface di, int i) {
|
||||
Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class);
|
||||
intent.putExtra("skipDetectMod", true);
|
||||
intent.putExtra("javaArgs", edit.getText().toString());
|
||||
startActivity(intent);
|
||||
}
|
||||
});
|
||||
builder.setPositiveButton(android.R.string.ok, (di, i) -> {
|
||||
Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class);
|
||||
intent.putExtra("skipDetectMod", true);
|
||||
intent.putExtra("javaArgs", edit.getText().toString());
|
||||
startActivity(intent);
|
||||
});
|
||||
dialog = builder.create();
|
||||
dialog.setView(edit);
|
||||
dialog.show();
|
||||
|
|
@ -131,11 +105,39 @@ public abstract class BaseLauncherActivity extends BaseActivity {
|
|||
} else if (canBack) {
|
||||
v.setEnabled(false);
|
||||
mTask = new MinecraftDownloaderTask(this);
|
||||
mTask.execute(mProfile.selectedVersion);
|
||||
mCrashView.resetCrashLog = true;
|
||||
LauncherProfiles.update();
|
||||
String selectedProfile = LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE,"");
|
||||
if (LauncherProfiles.mainProfileJson != null && LauncherProfiles.mainProfileJson.profiles != null && LauncherProfiles.mainProfileJson.profiles.containsKey(selectedProfile)) {
|
||||
MinecraftProfile prof = LauncherProfiles.mainProfileJson.profiles.get(selectedProfile);
|
||||
if (prof != null && prof.lastVersionId != null) {
|
||||
if (mProfile.accessToken.equals("0")) {
|
||||
String versionId = getVersionId(prof.lastVersionId);
|
||||
File verJsonFile = new File(Tools.DIR_HOME_VERSION,
|
||||
versionId + "/" + versionId + ".json");
|
||||
if (verJsonFile.exists()) {
|
||||
mTask.onPostExecute(null);
|
||||
return;
|
||||
}
|
||||
Tools.dialogOnUiThread(this,
|
||||
getString(R.string.global_error),
|
||||
getString(R.string.mcl_launch_error_localmode)
|
||||
);
|
||||
}else {
|
||||
mTask.execute(getVersionId(prof.lastVersionId));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static String getVersionId(String input) {
|
||||
Map<String,String> releaseTable = (Map<String,String>)ExtraCore.getValue(ExtraConstants.RELEASE_TABLE);
|
||||
if(releaseTable == null || releaseTable.isEmpty()) return input;
|
||||
if("latest-release".equals(input)) return releaseTable.get("release");
|
||||
if("latest-snapshot".equals(input)) return releaseTable.get("snapshot");
|
||||
return input;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (canBack) {
|
||||
|
|
@ -150,107 +152,60 @@ public abstract class BaseLauncherActivity extends BaseActivity {
|
|||
Tools.updateWindowSize(this);
|
||||
System.out.println("call to onPostResume; E");
|
||||
}
|
||||
|
||||
|
||||
public static void updateVersionSpinner(Context ctx, ArrayList<String> value, Spinner mVersionSelector, String defaultSelection) {
|
||||
if(value != null && value.size() > 0) {
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<String>(ctx, android.R.layout.simple_spinner_item, value);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice);
|
||||
mVersionSelector.setAdapter(adapter);
|
||||
mVersionSelector.setSelection(RefreshVersionListTask.selectAt(value, defaultSelection));
|
||||
return;
|
||||
}
|
||||
mVersionSelector.setSelection(RefreshVersionListTask.selectAt(PojavLauncherActivity.basicVersionList, defaultSelection));
|
||||
}
|
||||
@Override
|
||||
protected void onResume(){
|
||||
super.onResume();
|
||||
new RefreshVersionListTask(this).execute();
|
||||
if(listRefreshListener != null) {
|
||||
LauncherPreferences.DEFAULT_PREF.unregisterOnSharedPreferenceChangeListener(listRefreshListener);
|
||||
}
|
||||
listRefreshListener = (sharedPreferences, key) -> {
|
||||
if(key.startsWith("vertype_")) {
|
||||
System.out.println("Verlist update needed!");
|
||||
LauncherPreferences.PREF_VERTYPE_RELEASE = sharedPreferences.getBoolean("vertype_release",true);
|
||||
LauncherPreferences.PREF_VERTYPE_SNAPSHOT = sharedPreferences.getBoolean("vertype_snapshot",false);
|
||||
LauncherPreferences.PREF_VERTYPE_OLDALPHA = sharedPreferences.getBoolean("vertype_oldalpha",false);
|
||||
LauncherPreferences.PREF_VERTYPE_OLDBETA = sharedPreferences.getBoolean("vertype_oldbeta",false);
|
||||
new RefreshVersionListTask(this).execute();
|
||||
}
|
||||
};
|
||||
LauncherPreferences.DEFAULT_PREF.registerOnSharedPreferenceChangeListener(listRefreshListener);
|
||||
System.out.println("call to onResume");
|
||||
final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
final View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
System.out.println("call to onResume; E");
|
||||
}
|
||||
|
||||
SharedPreferences.OnSharedPreferenceChangeListener listRefreshListener = null;
|
||||
SharedPreferences.OnSharedPreferenceChangeListener profileEnableListener = null;
|
||||
@Override
|
||||
protected void onResumeFragments() {
|
||||
super.onResumeFragments();
|
||||
if(listRefreshListener == null) {
|
||||
final BaseLauncherActivity thiz = this;
|
||||
listRefreshListener = new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if(key.startsWith("vertype_")) {
|
||||
System.out.println("Verlist update needed!");
|
||||
new RefreshVersionListTask(thiz).execute();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
LauncherPreferences.DEFAULT_PREF.registerOnSharedPreferenceChangeListener(listRefreshListener);
|
||||
new RefreshVersionListTask(this).execute();
|
||||
System.out.println("call to onResumeFragments");
|
||||
mRuntimeConfigDialog = new MultiRTConfigDialog();
|
||||
mRuntimeConfigDialog.prepare(this);
|
||||
try{
|
||||
final ProgressDialog barrier = new ProgressDialog(this);
|
||||
barrier.setMessage(getString(R.string.global_waiting));
|
||||
barrier.setProgressStyle(barrier.STYLE_SPINNER);
|
||||
barrier.setCancelable(false);
|
||||
barrier.show();
|
||||
|
||||
new Thread(new Runnable(){
|
||||
((Button)findViewById(R.id.installJarButton)).setOnLongClickListener(view -> {
|
||||
installMod(true);
|
||||
return true;
|
||||
});
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (mConsoleView == null) {
|
||||
try {
|
||||
Thread.sleep(20);
|
||||
} catch (Throwable th) {}
|
||||
}
|
||||
|
||||
try {
|
||||
Thread.sleep(100);
|
||||
} catch (Throwable th) {}
|
||||
|
||||
runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try {
|
||||
mConsoleView.putLog("");
|
||||
barrier.dismiss();
|
||||
} catch (Throwable th) {
|
||||
startActivity(getIntent());
|
||||
finish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}).start();
|
||||
|
||||
File lastCrashFile = Tools.lastFileModified(Tools.DIR_HOME_CRASH);
|
||||
if(CrashFragment.isNewCrash(lastCrashFile) || !mCrashView.getLastCrash().isEmpty()){
|
||||
mCrashView.resetCrashLog = false;
|
||||
initTabs(2);
|
||||
|
||||
} /*else throw new Exception();*/
|
||||
} catch(Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
//TODO ADD CRASH CHECK AND FOCUS
|
||||
System.out.println("call to onResumeFragments; E");
|
||||
}
|
||||
public static String getFileName(Context ctx, Uri uri) {
|
||||
String result = null;
|
||||
if (uri.getScheme().equals("content")) {
|
||||
Cursor cursor = ctx.getContentResolver().query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = uri.getPath();
|
||||
int cut = result.lastIndexOf('/');
|
||||
if (cut != -1) {
|
||||
result = result.substring(cut + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
|
||||
super.onActivityResult(requestCode,resultCode,data);
|
||||
|
|
@ -260,62 +215,58 @@ public abstract class BaseLauncherActivity extends BaseActivity {
|
|||
barrier.setProgressStyle(barrier.STYLE_SPINNER);
|
||||
barrier.setCancelable(false);
|
||||
barrier.show();
|
||||
if (requestCode == MultiRTConfigDialog.MULTIRT_PICK_RUNTIME) {
|
||||
if (data != null) {
|
||||
final Uri uri = data.getData();
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
String name = getFileName(this, uri);
|
||||
MultiRTUtils.installRuntimeNamed(getContentResolver().openInputStream(uri), name,
|
||||
(resid, stuff) -> BaseLauncherActivity.this.runOnUiThread(
|
||||
() -> barrier.setMessage(BaseLauncherActivity.this.getString(resid, stuff))));
|
||||
MultiRTUtils.postPrepare(BaseLauncherActivity.this, name);
|
||||
} catch (IOException e) {
|
||||
Tools.showError(BaseLauncherActivity.this
|
||||
, e);
|
||||
}
|
||||
BaseLauncherActivity.this.runOnUiThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
barrier.dismiss();
|
||||
mRuntimeConfigDialog.refresh();
|
||||
mRuntimeConfigDialog.dialog.show();
|
||||
}
|
||||
});
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
} else if (requestCode == RUN_MOD_INSTALLER) {
|
||||
if (data != null) {
|
||||
final Uri uri = data.getData();
|
||||
barrier.setMessage(BaseLauncherActivity.this.getString(R.string.multirt_progress_caching));
|
||||
Thread t = new Thread(()->{
|
||||
try {
|
||||
final String name = getFileName(this, uri);
|
||||
final File modInstallerFile = new File(getCacheDir(), name);
|
||||
FileOutputStream fos = new FileOutputStream(modInstallerFile);
|
||||
IOUtils.copy(getContentResolver().openInputStream(uri), fos);
|
||||
fos.close();
|
||||
BaseLauncherActivity.this.runOnUiThread(() -> {
|
||||
barrier.dismiss();
|
||||
Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class);
|
||||
intent.putExtra("modFile", modInstallerFile);
|
||||
startActivity(intent);
|
||||
});
|
||||
}catch(IOException e) {
|
||||
Tools.showError(BaseLauncherActivity.this,e);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Catching touch exception
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
return super.onTouchEvent(event);
|
||||
// Install the runtime
|
||||
if (requestCode == MultiRTConfigDialog.MULTIRT_PICK_RUNTIME) {
|
||||
if (data == null) return;
|
||||
|
||||
final Uri uri = data.getData();
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
String name = getFileName(this, uri);
|
||||
MultiRTUtils.installRuntimeNamed(getContentResolver().openInputStream(uri), name,
|
||||
(resid, stuff) -> BaseLauncherActivity.this.runOnUiThread(
|
||||
() -> barrier.setMessage(BaseLauncherActivity.this.getString(resid, stuff))));
|
||||
MultiRTUtils.postPrepare(BaseLauncherActivity.this, name);
|
||||
} catch (IOException e) {
|
||||
Tools.showError(BaseLauncherActivity.this, e);
|
||||
}
|
||||
BaseLauncherActivity.this.runOnUiThread(() -> {
|
||||
barrier.dismiss();
|
||||
mRuntimeConfigDialog.refresh();
|
||||
mRuntimeConfigDialog.mDialog.show();
|
||||
});
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
// Run a mod installer
|
||||
if (requestCode == RUN_MOD_INSTALLER) {
|
||||
if (data == null) return;
|
||||
|
||||
final Uri uri = data.getData();
|
||||
barrier.setMessage(BaseLauncherActivity.this.getString(R.string.multirt_progress_caching));
|
||||
Thread t = new Thread(()->{
|
||||
try {
|
||||
final String name = getFileName(this, uri);
|
||||
final File modInstallerFile = new File(getCacheDir(), name);
|
||||
FileOutputStream fos = new FileOutputStream(modInstallerFile);
|
||||
IOUtils.copy(getContentResolver().openInputStream(uri), fos);
|
||||
fos.close();
|
||||
BaseLauncherActivity.this.runOnUiThread(() -> {
|
||||
barrier.dismiss();
|
||||
Intent intent = new Intent(BaseLauncherActivity.this, JavaGUILauncherActivity.class);
|
||||
intent.putExtra("modFile", modInstallerFile);
|
||||
startActivity(intent);
|
||||
});
|
||||
}catch(IOException e) {
|
||||
Tools.showError(BaseLauncherActivity.this,e);
|
||||
}
|
||||
});
|
||||
t.start();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void initTabs(int pageIndex);
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -5,13 +5,12 @@ import android.content.*;
|
|||
import android.os.*;
|
||||
|
||||
import androidx.appcompat.app.*;
|
||||
import androidx.preference.*;
|
||||
import android.view.*;
|
||||
|
||||
import android.widget.*;
|
||||
|
||||
import androidx.drawerlayout.widget.DrawerLayout;
|
||||
|
||||
import com.google.android.material.navigation.NavigationView;
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
import com.kdt.pickafile.*;
|
||||
import java.io.*;
|
||||
|
||||
|
|
@ -19,18 +18,13 @@ import net.kdt.pojavlaunch.prefs.*;
|
|||
import net.kdt.pojavlaunch.customcontrols.*;
|
||||
|
||||
|
||||
|
||||
public class CustomControlsActivity extends BaseActivity
|
||||
{
|
||||
private DrawerLayout drawerLayout;
|
||||
private NavigationView navDrawer;
|
||||
private ControlLayout ctrlLayout;
|
||||
|
||||
private SharedPreferences mPref;
|
||||
public class CustomControlsActivity extends BaseActivity {
|
||||
private DrawerLayout mDrawerLayout;
|
||||
private ListView mDrawerNavigationView;
|
||||
private ControlLayout mControlLayout;
|
||||
|
||||
public boolean isModified = false;
|
||||
public boolean isFromMainActivity = false;
|
||||
private static String selectedName = "new_control";
|
||||
private static String sSelectedName = "new_control";
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
|
@ -41,48 +35,62 @@ public class CustomControlsActivity extends BaseActivity
|
|||
// setTheme(androidx.appcompat.R.style.Theme_AppCompat_Translucent);
|
||||
}
|
||||
|
||||
setContentView(R.layout.control_mapping);
|
||||
|
||||
mPref = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
setContentView(R.layout.activity_custom_controls);
|
||||
|
||||
ctrlLayout = (ControlLayout) findViewById(R.id.customctrl_controllayout);
|
||||
mControlLayout = (ControlLayout) findViewById(R.id.customctrl_controllayout);
|
||||
mDrawerLayout = (DrawerLayout) findViewById(R.id.customctrl_drawerlayout);
|
||||
mDrawerNavigationView = (ListView) findViewById(R.id.customctrl_navigation_view);
|
||||
|
||||
// Menu
|
||||
drawerLayout = (DrawerLayout) findViewById(R.id.customctrl_drawerlayout);
|
||||
|
||||
navDrawer = (NavigationView) findViewById(R.id.customctrl_navigation_view);
|
||||
navDrawer.setNavigationItemSelectedListener(
|
||||
new NavigationView.OnNavigationItemSelectedListener() {
|
||||
@Override
|
||||
public boolean onNavigationItemSelected(MenuItem menuItem) {
|
||||
mDrawerNavigationView.setAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1,getResources().getStringArray(R.array.menu_customcontrol)));
|
||||
mDrawerNavigationView.setOnItemClickListener((parent, view, position, id) -> {
|
||||
switch(position) {
|
||||
case 0:
|
||||
mControlLayout.addControlButton(new ControlData("New"));
|
||||
break;
|
||||
case 1:
|
||||
mControlLayout.addDrawer(new ControlDrawerData());
|
||||
break;
|
||||
case 2:
|
||||
load(mControlLayout);
|
||||
break;
|
||||
case 3:
|
||||
save(false, mControlLayout);
|
||||
break;
|
||||
case 4:
|
||||
dialogSelectDefaultCtrl(mControlLayout);
|
||||
break;
|
||||
}
|
||||
mDrawerLayout.closeDrawers();
|
||||
});
|
||||
/*mDrawerNavigationView.setNavigationItemSelectedListener(
|
||||
menuItem -> {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.menu_ctrl_load:
|
||||
load(ctrlLayout);
|
||||
load(mControlLayout);
|
||||
break;
|
||||
case R.id.menu_ctrl_add:
|
||||
ctrlLayout.addControlButton(new ControlData("New"));
|
||||
mControlLayout.addControlButton(new ControlData("New"));
|
||||
break;
|
||||
case R.id.menu_ctrl_add_drawer:
|
||||
ctrlLayout.addDrawer(new ControlDrawerData());
|
||||
mControlLayout.addDrawer(new ControlDrawerData());
|
||||
break;
|
||||
case R.id.menu_ctrl_selectdefault:
|
||||
dialogSelectDefaultCtrl(ctrlLayout);
|
||||
dialogSelectDefaultCtrl(mControlLayout);
|
||||
break;
|
||||
case R.id.menu_ctrl_save:
|
||||
save(false,ctrlLayout);
|
||||
save(false, mControlLayout);
|
||||
break;
|
||||
}
|
||||
//Toast.makeText(MainActivity.this, menuItem.getTitle() + ":" + menuItem.getItemId(), Toast.LENGTH_SHORT).show();
|
||||
|
||||
drawerLayout.closeDrawers();
|
||||
mDrawerLayout.closeDrawers();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
});
|
||||
*/
|
||||
mControlLayout.setActivity(this);
|
||||
mControlLayout.setModifiable(true);
|
||||
|
||||
ctrlLayout.setActivity(this);
|
||||
ctrlLayout.setModifiable(true);
|
||||
|
||||
loadControl(LauncherPreferences.PREF_DEFAULTCTRL_PATH,ctrlLayout);
|
||||
loadControl(LauncherPreferences.PREF_DEFAULTCTRL_PATH, mControlLayout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -93,18 +101,7 @@ public class CustomControlsActivity extends BaseActivity
|
|||
return;
|
||||
}
|
||||
|
||||
save(true,ctrlLayout);
|
||||
}
|
||||
|
||||
private static void setDefaultControlJson(String path,ControlLayout ctrlLayout) {
|
||||
try {
|
||||
// Load before save to make sure control is not error
|
||||
ctrlLayout.loadLayout(Tools.GLOBAL_GSON.fromJson(Tools.read(path), CustomControls.class));
|
||||
LauncherPreferences.DEFAULT_PREF.edit().putString("defaultCtrl", path).commit();
|
||||
LauncherPreferences.PREF_DEFAULTCTRL_PATH = path;
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(ctrlLayout.getContext(), th);
|
||||
}
|
||||
save(true, mControlLayout);
|
||||
}
|
||||
|
||||
public static void dialogSelectDefaultCtrl(final ControlLayout layout) {
|
||||
|
|
@ -127,18 +124,12 @@ public class CustomControlsActivity extends BaseActivity
|
|||
dialog.show();
|
||||
}
|
||||
|
||||
private static String doSaveCtrl(String name, final ControlLayout layout) throws Exception {
|
||||
String jsonPath = Tools.CTRLMAP_PATH + "/" + name + ".json";
|
||||
layout.saveLayout(jsonPath);
|
||||
|
||||
return jsonPath;
|
||||
}
|
||||
|
||||
public static void save(final boolean exit, final ControlLayout layout) {
|
||||
final Context ctx = layout.getContext();
|
||||
final EditText edit = new EditText(ctx);
|
||||
edit.setSingleLine();
|
||||
edit.setText(selectedName);
|
||||
edit.setText(sSelectedName);
|
||||
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
|
||||
builder.setTitle(R.string.global_save);
|
||||
|
|
@ -162,51 +153,38 @@ public class CustomControlsActivity extends BaseActivity
|
|||
});
|
||||
}
|
||||
final AlertDialog dialog = builder.create();
|
||||
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
|
||||
dialog.setOnShowListener(dialogInterface -> {
|
||||
|
||||
@Override
|
||||
public void onShow(DialogInterface dialogInterface) {
|
||||
|
||||
Button button = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(new View.OnClickListener() {
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
if (edit.getText().toString().isEmpty()) {
|
||||
edit.setError(ctx.getResources().getString(R.string.global_error_field_empty));
|
||||
} else {
|
||||
try {
|
||||
String jsonPath = doSaveCtrl(edit.getText().toString(),layout);
|
||||
|
||||
Toast.makeText(ctx, ctx.getString(R.string.global_save) + ": " + jsonPath, Toast.LENGTH_SHORT).show();
|
||||
|
||||
dialog.dismiss();
|
||||
if (exit) {
|
||||
if(ctx instanceof MainActivity) {
|
||||
((MainActivity) ctx).leaveCustomControls();
|
||||
}else{
|
||||
((Activity)ctx).onBackPressed();
|
||||
}
|
||||
//CustomControlsActivity.super.onBackPressed();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(ctx, th, exit);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Button button = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
|
||||
button.setOnClickListener(view -> {
|
||||
if (edit.getText().toString().isEmpty()) {
|
||||
edit.setError(ctx.getResources().getString(R.string.global_error_field_empty));
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String jsonPath = doSaveCtrl(edit.getText().toString(),layout);
|
||||
Toast.makeText(ctx, ctx.getString(R.string.global_save) + ": " + jsonPath, Toast.LENGTH_SHORT).show();
|
||||
|
||||
dialog.dismiss();
|
||||
if (!exit) return;
|
||||
|
||||
if(ctx instanceof MainActivity) {
|
||||
((MainActivity) ctx).leaveCustomControls();
|
||||
}else{
|
||||
((Activity)ctx).onBackPressed();
|
||||
}
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(ctx, th, exit);
|
||||
}
|
||||
|
||||
});
|
||||
});
|
||||
dialog.show();
|
||||
|
||||
}
|
||||
|
||||
public static void load(final ControlLayout layout) {
|
||||
/*ControlJsonSelector sel = new ControlJsonSelector(layout.getContext(), R.string.global_load);
|
||||
sel.setFinishCallback((f)->{
|
||||
loadControl(f.getAbsolutePath(),layout);
|
||||
});
|
||||
sel.show();*/
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(layout.getContext());
|
||||
builder.setTitle(R.string.global_load);
|
||||
builder.setPositiveButton(android.R.string.cancel, null);
|
||||
|
|
@ -227,12 +205,30 @@ public class CustomControlsActivity extends BaseActivity
|
|||
dialog.show();
|
||||
}
|
||||
|
||||
private static void setDefaultControlJson(String path,ControlLayout ctrlLayout) {
|
||||
// Load before save to make sure control is not error
|
||||
try {
|
||||
ctrlLayout.loadLayout(Tools.GLOBAL_GSON.fromJson(Tools.read(path), CustomControls.class));
|
||||
LauncherPreferences.DEFAULT_PREF.edit().putString("defaultCtrl", path).apply();
|
||||
LauncherPreferences.PREF_DEFAULTCTRL_PATH = path;
|
||||
} catch (IOException| JsonSyntaxException exception) {
|
||||
Tools.showError(ctrlLayout.getContext(), exception);
|
||||
}
|
||||
}
|
||||
|
||||
private static String doSaveCtrl(String name, final ControlLayout layout) throws Exception {
|
||||
String jsonPath = Tools.CTRLMAP_PATH + "/" + name + ".json";
|
||||
layout.saveLayout(jsonPath);
|
||||
|
||||
return jsonPath;
|
||||
}
|
||||
|
||||
private static void loadControl(String path,ControlLayout layout) {
|
||||
try {
|
||||
layout.loadLayout(path);
|
||||
selectedName = new File(path).getName();
|
||||
sSelectedName = new File(path).getName();
|
||||
// Remove `.json`
|
||||
selectedName = selectedName.substring(0, selectedName.length() - 5);
|
||||
sSelectedName = sSelectedName.substring(0, sSelectedName.length() - 5);
|
||||
} catch (Exception e) {
|
||||
Tools.showError(layout.getContext(), e);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,16 +0,0 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.content.*;
|
||||
import android.net.*;
|
||||
import androidx.browser.customtabs.*;
|
||||
|
||||
public class CustomTabs {
|
||||
|
||||
public static void openTab(Context context, String url) {
|
||||
CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder();
|
||||
builder.setShowTitle(true);
|
||||
|
||||
CustomTabsIntent customTabsIntent = builder.build();
|
||||
customTabsIntent.launchUrl(context, Uri.parse(url));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,67 +0,0 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
|
||||
|
||||
/**
|
||||
* Class aiming at better detecting double tap events for EVERY POINTER
|
||||
* Only uses the least amount of events possible,
|
||||
* since we aren't guaranteed to have all events in order
|
||||
*/
|
||||
public class DoubleTapDetector {
|
||||
|
||||
private final static int DOUBLE_TAP_MIN_DELTA_MS = 50;
|
||||
private final static int DOUBLE_TAP_MAX_DELTA_MS = 300;
|
||||
private final static int DOUBLE_TAP_SLOP_SQUARE_PX = (int) Math.pow(Tools.dpToPx(100), 2);
|
||||
|
||||
private long mLastEventTime = 0;
|
||||
private float mLastX = 9999;
|
||||
private float mLastY = 9999;
|
||||
|
||||
/**
|
||||
* A function to call when you have a touch event.
|
||||
* @param e The MotionEvent to inspect
|
||||
* @return whether or not a double tap happened for a pointer
|
||||
*/
|
||||
public boolean onTouchEvent(MotionEvent e){
|
||||
int eventAction = e.getActionMasked();
|
||||
int pointerIndex;
|
||||
|
||||
//Get the pointer index we want to look at
|
||||
if(eventAction == ACTION_DOWN) pointerIndex = 0;
|
||||
else if(eventAction == ACTION_POINTER_DOWN) pointerIndex = e.getActionIndex();
|
||||
else return false;
|
||||
|
||||
float eventX = e.getX(pointerIndex);
|
||||
float eventY = e.getY(pointerIndex);
|
||||
long eventTime = e.getEventTime();
|
||||
|
||||
long deltaTime = eventTime - mLastEventTime;
|
||||
if(deltaTime > DOUBLE_TAP_MIN_DELTA_MS && deltaTime < DOUBLE_TAP_MAX_DELTA_MS){
|
||||
int deltaX = (int) mLastX - (int) eventX;
|
||||
int deltaY = (int) mLastY - (int) eventY;
|
||||
if((deltaX*deltaX + deltaY*deltaY) < DOUBLE_TAP_SLOP_SQUARE_PX){
|
||||
//Then I guess there is a double tap :thonk:
|
||||
resetDoubleTapState();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
mLastEventTime = eventTime;
|
||||
mLastX = eventX;
|
||||
mLastY = eventY;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the double tap values.
|
||||
*/
|
||||
private void resetDoubleTapState(){
|
||||
mLastEventTime = 0;
|
||||
mLastX = 9999;
|
||||
mLastY = 9999;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,8 +1,8 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.view.KeyEvent;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import android.view.KeyEvent;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
|
|
@ -14,168 +14,158 @@ public class EfficientAndroidLWJGLKeycode {
|
|||
//The key being the android keycode from a KeyEvent
|
||||
//The value its LWJGL equivalent.
|
||||
private static final int KEYCODE_COUNT = 103;
|
||||
private static final int[] androidKeycodes = new int[KEYCODE_COUNT];
|
||||
private static final short[] LWJGLKeycodes = new short[KEYCODE_COUNT];
|
||||
private static final int[] sAndroidKeycodes = new int[KEYCODE_COUNT];
|
||||
private static final short[] sLwjglKeycodes = new short[KEYCODE_COUNT];
|
||||
private static String[] androidKeyNameArray; /* = new String[androidKeycodes.length]; */
|
||||
private static int mTmpCount = 0;
|
||||
|
||||
static {
|
||||
|
||||
/* BINARY SEARCH IS PERFORMED ON THE androidKeycodes ARRAY !
|
||||
WHEN ADDING A MAPPING, ADD IT SO THE androidKeycodes ARRAY STAYS SORTED ! */
|
||||
// Mapping Android Keycodes to LWJGL Keycodes
|
||||
add(KeyEvent.KEYCODE_UNKNOWN,LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN);
|
||||
add(KeyEvent.KEYCODE_HOME, LWJGLGLFWKeycode.GLFW_KEY_HOME);
|
||||
add(KeyEvent.KEYCODE_UNKNOWN, LwjglGlfwKeycode.GLFW_KEY_UNKNOWN);
|
||||
add(KeyEvent.KEYCODE_HOME, LwjglGlfwKeycode.GLFW_KEY_HOME);
|
||||
// Escape key
|
||||
add(KeyEvent.KEYCODE_BACK, LWJGLGLFWKeycode.GLFW_KEY_ESCAPE);
|
||||
add(KeyEvent.KEYCODE_BACK, LwjglGlfwKeycode.GLFW_KEY_ESCAPE);
|
||||
|
||||
// 0-9 keys
|
||||
add(KeyEvent.KEYCODE_0, LWJGLGLFWKeycode.GLFW_KEY_0); //7
|
||||
add(KeyEvent.KEYCODE_1, LWJGLGLFWKeycode.GLFW_KEY_1);
|
||||
add(KeyEvent.KEYCODE_2, LWJGLGLFWKeycode.GLFW_KEY_2);
|
||||
add(KeyEvent.KEYCODE_3, LWJGLGLFWKeycode.GLFW_KEY_3);
|
||||
add(KeyEvent.KEYCODE_4, LWJGLGLFWKeycode.GLFW_KEY_4);
|
||||
add(KeyEvent.KEYCODE_5, LWJGLGLFWKeycode.GLFW_KEY_5);
|
||||
add(KeyEvent.KEYCODE_6, LWJGLGLFWKeycode.GLFW_KEY_6);
|
||||
add(KeyEvent.KEYCODE_7, LWJGLGLFWKeycode.GLFW_KEY_7);
|
||||
add(KeyEvent.KEYCODE_8, LWJGLGLFWKeycode.GLFW_KEY_8);
|
||||
add(KeyEvent.KEYCODE_9, LWJGLGLFWKeycode.GLFW_KEY_9); //16
|
||||
add(KeyEvent.KEYCODE_0, LwjglGlfwKeycode.GLFW_KEY_0); //7
|
||||
add(KeyEvent.KEYCODE_1, LwjglGlfwKeycode.GLFW_KEY_1);
|
||||
add(KeyEvent.KEYCODE_2, LwjglGlfwKeycode.GLFW_KEY_2);
|
||||
add(KeyEvent.KEYCODE_3, LwjglGlfwKeycode.GLFW_KEY_3);
|
||||
add(KeyEvent.KEYCODE_4, LwjglGlfwKeycode.GLFW_KEY_4);
|
||||
add(KeyEvent.KEYCODE_5, LwjglGlfwKeycode.GLFW_KEY_5);
|
||||
add(KeyEvent.KEYCODE_6, LwjglGlfwKeycode.GLFW_KEY_6);
|
||||
add(KeyEvent.KEYCODE_7, LwjglGlfwKeycode.GLFW_KEY_7);
|
||||
add(KeyEvent.KEYCODE_8, LwjglGlfwKeycode.GLFW_KEY_8);
|
||||
add(KeyEvent.KEYCODE_9, LwjglGlfwKeycode.GLFW_KEY_9); //16
|
||||
|
||||
add(KeyEvent.KEYCODE_POUND,LWJGLGLFWKeycode.GLFW_KEY_3);
|
||||
add(KeyEvent.KEYCODE_POUND, LwjglGlfwKeycode.GLFW_KEY_3);
|
||||
|
||||
// Arrow keys
|
||||
add(KeyEvent.KEYCODE_DPAD_UP, LWJGLGLFWKeycode.GLFW_KEY_UP); //19
|
||||
add(KeyEvent.KEYCODE_DPAD_DOWN, LWJGLGLFWKeycode.GLFW_KEY_DOWN);
|
||||
add(KeyEvent.KEYCODE_DPAD_LEFT, LWJGLGLFWKeycode.GLFW_KEY_LEFT);
|
||||
add(KeyEvent.KEYCODE_DPAD_RIGHT, LWJGLGLFWKeycode.GLFW_KEY_RIGHT); //22
|
||||
add(KeyEvent.KEYCODE_DPAD_UP, LwjglGlfwKeycode.GLFW_KEY_UP); //19
|
||||
add(KeyEvent.KEYCODE_DPAD_DOWN, LwjglGlfwKeycode.GLFW_KEY_DOWN);
|
||||
add(KeyEvent.KEYCODE_DPAD_LEFT, LwjglGlfwKeycode.GLFW_KEY_LEFT);
|
||||
add(KeyEvent.KEYCODE_DPAD_RIGHT, LwjglGlfwKeycode.GLFW_KEY_RIGHT); //22
|
||||
|
||||
// A-Z keys
|
||||
add(KeyEvent.KEYCODE_A, LWJGLGLFWKeycode.GLFW_KEY_A); //29
|
||||
add(KeyEvent.KEYCODE_B, LWJGLGLFWKeycode.GLFW_KEY_B);
|
||||
add(KeyEvent.KEYCODE_C, LWJGLGLFWKeycode.GLFW_KEY_C);
|
||||
add(KeyEvent.KEYCODE_D, LWJGLGLFWKeycode.GLFW_KEY_D);
|
||||
add(KeyEvent.KEYCODE_E, LWJGLGLFWKeycode.GLFW_KEY_E);
|
||||
add(KeyEvent.KEYCODE_F, LWJGLGLFWKeycode.GLFW_KEY_F);
|
||||
add(KeyEvent.KEYCODE_G, LWJGLGLFWKeycode.GLFW_KEY_G);
|
||||
add(KeyEvent.KEYCODE_H, LWJGLGLFWKeycode.GLFW_KEY_H);
|
||||
add(KeyEvent.KEYCODE_I, LWJGLGLFWKeycode.GLFW_KEY_I);
|
||||
add(KeyEvent.KEYCODE_J, LWJGLGLFWKeycode.GLFW_KEY_J);
|
||||
add(KeyEvent.KEYCODE_K, LWJGLGLFWKeycode.GLFW_KEY_K);
|
||||
add(KeyEvent.KEYCODE_L, LWJGLGLFWKeycode.GLFW_KEY_L);
|
||||
add(KeyEvent.KEYCODE_M, LWJGLGLFWKeycode.GLFW_KEY_M);
|
||||
add(KeyEvent.KEYCODE_N, LWJGLGLFWKeycode.GLFW_KEY_N);
|
||||
add(KeyEvent.KEYCODE_O, LWJGLGLFWKeycode.GLFW_KEY_O);
|
||||
add(KeyEvent.KEYCODE_P, LWJGLGLFWKeycode.GLFW_KEY_P);
|
||||
add(KeyEvent.KEYCODE_Q, LWJGLGLFWKeycode.GLFW_KEY_Q);
|
||||
add(KeyEvent.KEYCODE_R, LWJGLGLFWKeycode.GLFW_KEY_R);
|
||||
add(KeyEvent.KEYCODE_S, LWJGLGLFWKeycode.GLFW_KEY_S);
|
||||
add(KeyEvent.KEYCODE_T, LWJGLGLFWKeycode.GLFW_KEY_T);
|
||||
add(KeyEvent.KEYCODE_U, LWJGLGLFWKeycode.GLFW_KEY_U);
|
||||
add(KeyEvent.KEYCODE_V, LWJGLGLFWKeycode.GLFW_KEY_V);
|
||||
add(KeyEvent.KEYCODE_W, LWJGLGLFWKeycode.GLFW_KEY_W);
|
||||
add(KeyEvent.KEYCODE_X, LWJGLGLFWKeycode.GLFW_KEY_X);
|
||||
add(KeyEvent.KEYCODE_Y, LWJGLGLFWKeycode.GLFW_KEY_Y);
|
||||
add(KeyEvent.KEYCODE_Z, LWJGLGLFWKeycode.GLFW_KEY_Z); //54
|
||||
add(KeyEvent.KEYCODE_A, LwjglGlfwKeycode.GLFW_KEY_A); //29
|
||||
add(KeyEvent.KEYCODE_B, LwjglGlfwKeycode.GLFW_KEY_B);
|
||||
add(KeyEvent.KEYCODE_C, LwjglGlfwKeycode.GLFW_KEY_C);
|
||||
add(KeyEvent.KEYCODE_D, LwjglGlfwKeycode.GLFW_KEY_D);
|
||||
add(KeyEvent.KEYCODE_E, LwjglGlfwKeycode.GLFW_KEY_E);
|
||||
add(KeyEvent.KEYCODE_F, LwjglGlfwKeycode.GLFW_KEY_F);
|
||||
add(KeyEvent.KEYCODE_G, LwjglGlfwKeycode.GLFW_KEY_G);
|
||||
add(KeyEvent.KEYCODE_H, LwjglGlfwKeycode.GLFW_KEY_H);
|
||||
add(KeyEvent.KEYCODE_I, LwjglGlfwKeycode.GLFW_KEY_I);
|
||||
add(KeyEvent.KEYCODE_J, LwjglGlfwKeycode.GLFW_KEY_J);
|
||||
add(KeyEvent.KEYCODE_K, LwjglGlfwKeycode.GLFW_KEY_K);
|
||||
add(KeyEvent.KEYCODE_L, LwjglGlfwKeycode.GLFW_KEY_L);
|
||||
add(KeyEvent.KEYCODE_M, LwjglGlfwKeycode.GLFW_KEY_M);
|
||||
add(KeyEvent.KEYCODE_N, LwjglGlfwKeycode.GLFW_KEY_N);
|
||||
add(KeyEvent.KEYCODE_O, LwjglGlfwKeycode.GLFW_KEY_O);
|
||||
add(KeyEvent.KEYCODE_P, LwjglGlfwKeycode.GLFW_KEY_P);
|
||||
add(KeyEvent.KEYCODE_Q, LwjglGlfwKeycode.GLFW_KEY_Q);
|
||||
add(KeyEvent.KEYCODE_R, LwjglGlfwKeycode.GLFW_KEY_R);
|
||||
add(KeyEvent.KEYCODE_S, LwjglGlfwKeycode.GLFW_KEY_S);
|
||||
add(KeyEvent.KEYCODE_T, LwjglGlfwKeycode.GLFW_KEY_T);
|
||||
add(KeyEvent.KEYCODE_U, LwjglGlfwKeycode.GLFW_KEY_U);
|
||||
add(KeyEvent.KEYCODE_V, LwjglGlfwKeycode.GLFW_KEY_V);
|
||||
add(KeyEvent.KEYCODE_W, LwjglGlfwKeycode.GLFW_KEY_W);
|
||||
add(KeyEvent.KEYCODE_X, LwjglGlfwKeycode.GLFW_KEY_X);
|
||||
add(KeyEvent.KEYCODE_Y, LwjglGlfwKeycode.GLFW_KEY_Y);
|
||||
add(KeyEvent.KEYCODE_Z, LwjglGlfwKeycode.GLFW_KEY_Z); //54
|
||||
|
||||
|
||||
add(KeyEvent.KEYCODE_COMMA, LWJGLGLFWKeycode.GLFW_KEY_COMMA);
|
||||
add(KeyEvent.KEYCODE_PERIOD, LWJGLGLFWKeycode.GLFW_KEY_PERIOD);
|
||||
add(KeyEvent.KEYCODE_COMMA, LwjglGlfwKeycode.GLFW_KEY_COMMA);
|
||||
add(KeyEvent.KEYCODE_PERIOD, LwjglGlfwKeycode.GLFW_KEY_PERIOD);
|
||||
|
||||
// Alt keys
|
||||
add(KeyEvent.KEYCODE_ALT_LEFT, LWJGLGLFWKeycode.GLFW_KEY_LEFT_ALT);
|
||||
add(KeyEvent.KEYCODE_ALT_RIGHT, LWJGLGLFWKeycode.GLFW_KEY_RIGHT_ALT);
|
||||
add(KeyEvent.KEYCODE_ALT_LEFT, LwjglGlfwKeycode.GLFW_KEY_LEFT_ALT);
|
||||
add(KeyEvent.KEYCODE_ALT_RIGHT, LwjglGlfwKeycode.GLFW_KEY_RIGHT_ALT);
|
||||
|
||||
// Shift keys
|
||||
add(KeyEvent.KEYCODE_SHIFT_LEFT, LWJGLGLFWKeycode.GLFW_KEY_LEFT_SHIFT);
|
||||
add(KeyEvent.KEYCODE_SHIFT_RIGHT, LWJGLGLFWKeycode.GLFW_KEY_RIGHT_SHIFT);
|
||||
add(KeyEvent.KEYCODE_SHIFT_LEFT, LwjglGlfwKeycode.GLFW_KEY_LEFT_SHIFT);
|
||||
add(KeyEvent.KEYCODE_SHIFT_RIGHT, LwjglGlfwKeycode.GLFW_KEY_RIGHT_SHIFT);
|
||||
|
||||
add(KeyEvent.KEYCODE_TAB, LWJGLGLFWKeycode.GLFW_KEY_TAB);
|
||||
add(KeyEvent.KEYCODE_SPACE, LWJGLGLFWKeycode.GLFW_KEY_SPACE);
|
||||
add(KeyEvent.KEYCODE_ENTER, LWJGLGLFWKeycode.GLFW_KEY_ENTER); //66
|
||||
add(KeyEvent.KEYCODE_DEL, LWJGLGLFWKeycode.GLFW_KEY_BACKSPACE); // Backspace
|
||||
add(KeyEvent.KEYCODE_GRAVE, LWJGLGLFWKeycode.GLFW_KEY_GRAVE_ACCENT);
|
||||
add(KeyEvent.KEYCODE_MINUS, LWJGLGLFWKeycode.GLFW_KEY_MINUS);
|
||||
add(KeyEvent.KEYCODE_EQUALS, LWJGLGLFWKeycode.GLFW_KEY_EQUAL);
|
||||
add(KeyEvent.KEYCODE_LEFT_BRACKET, LWJGLGLFWKeycode.GLFW_KEY_LEFT_BRACKET);
|
||||
add(KeyEvent.KEYCODE_RIGHT_BRACKET, LWJGLGLFWKeycode.GLFW_KEY_RIGHT_BRACKET);
|
||||
add(KeyEvent.KEYCODE_BACKSLASH, LWJGLGLFWKeycode.GLFW_KEY_BACKSLASH);
|
||||
add(KeyEvent.KEYCODE_SEMICOLON, LWJGLGLFWKeycode.GLFW_KEY_SEMICOLON); //74
|
||||
add(KeyEvent.KEYCODE_TAB, LwjglGlfwKeycode.GLFW_KEY_TAB);
|
||||
add(KeyEvent.KEYCODE_SPACE, LwjglGlfwKeycode.GLFW_KEY_SPACE);
|
||||
add(KeyEvent.KEYCODE_ENTER, LwjglGlfwKeycode.GLFW_KEY_ENTER); //66
|
||||
add(KeyEvent.KEYCODE_DEL, LwjglGlfwKeycode.GLFW_KEY_BACKSPACE); // Backspace
|
||||
add(KeyEvent.KEYCODE_GRAVE, LwjglGlfwKeycode.GLFW_KEY_GRAVE_ACCENT);
|
||||
add(KeyEvent.KEYCODE_MINUS, LwjglGlfwKeycode.GLFW_KEY_MINUS);
|
||||
add(KeyEvent.KEYCODE_EQUALS, LwjglGlfwKeycode.GLFW_KEY_EQUAL);
|
||||
add(KeyEvent.KEYCODE_LEFT_BRACKET, LwjglGlfwKeycode.GLFW_KEY_LEFT_BRACKET);
|
||||
add(KeyEvent.KEYCODE_RIGHT_BRACKET, LwjglGlfwKeycode.GLFW_KEY_RIGHT_BRACKET);
|
||||
add(KeyEvent.KEYCODE_BACKSLASH, LwjglGlfwKeycode.GLFW_KEY_BACKSLASH);
|
||||
add(KeyEvent.KEYCODE_SEMICOLON, LwjglGlfwKeycode.GLFW_KEY_SEMICOLON); //74
|
||||
|
||||
add(KeyEvent.KEYCODE_SLASH, LWJGLGLFWKeycode.GLFW_KEY_SLASH); //76
|
||||
add(KeyEvent.KEYCODE_AT,LWJGLGLFWKeycode.GLFW_KEY_2);
|
||||
add(KeyEvent.KEYCODE_SLASH, LwjglGlfwKeycode.GLFW_KEY_SLASH); //76
|
||||
add(KeyEvent.KEYCODE_AT, LwjglGlfwKeycode.GLFW_KEY_2);
|
||||
|
||||
add(KeyEvent.KEYCODE_PLUS, LWJGLGLFWKeycode.GLFW_KEY_KP_ADD);
|
||||
add(KeyEvent.KEYCODE_PLUS, LwjglGlfwKeycode.GLFW_KEY_KP_ADD);
|
||||
|
||||
// Page keys
|
||||
add(KeyEvent.KEYCODE_PAGE_UP, LWJGLGLFWKeycode.GLFW_KEY_PAGE_UP); //92
|
||||
add(KeyEvent.KEYCODE_PAGE_DOWN, LWJGLGLFWKeycode.GLFW_KEY_PAGE_DOWN);
|
||||
add(KeyEvent.KEYCODE_PAGE_UP, LwjglGlfwKeycode.GLFW_KEY_PAGE_UP); //92
|
||||
add(KeyEvent.KEYCODE_PAGE_DOWN, LwjglGlfwKeycode.GLFW_KEY_PAGE_DOWN);
|
||||
|
||||
add(KeyEvent.KEYCODE_ESCAPE, LWJGLGLFWKeycode.GLFW_KEY_ESCAPE);
|
||||
add(KeyEvent.KEYCODE_ESCAPE, LwjglGlfwKeycode.GLFW_KEY_ESCAPE);
|
||||
|
||||
// Control keys
|
||||
add(KeyEvent.KEYCODE_CTRL_LEFT, LWJGLGLFWKeycode.GLFW_KEY_LEFT_CONTROL);
|
||||
add(KeyEvent.KEYCODE_CTRL_RIGHT, LWJGLGLFWKeycode.GLFW_KEY_RIGHT_CONTROL);
|
||||
add(KeyEvent.KEYCODE_CTRL_LEFT, LwjglGlfwKeycode.GLFW_KEY_LEFT_CONTROL);
|
||||
add(KeyEvent.KEYCODE_CTRL_RIGHT, LwjglGlfwKeycode.GLFW_KEY_RIGHT_CONTROL);
|
||||
|
||||
add(KeyEvent.KEYCODE_CAPS_LOCK, LWJGLGLFWKeycode.GLFW_KEY_CAPS_LOCK);
|
||||
add(KeyEvent.KEYCODE_BREAK, LWJGLGLFWKeycode.GLFW_KEY_PAUSE);
|
||||
add(KeyEvent.KEYCODE_INSERT, LWJGLGLFWKeycode.GLFW_KEY_INSERT);
|
||||
add(KeyEvent.KEYCODE_CAPS_LOCK, LwjglGlfwKeycode.GLFW_KEY_CAPS_LOCK);
|
||||
add(KeyEvent.KEYCODE_BREAK, LwjglGlfwKeycode.GLFW_KEY_PAUSE);
|
||||
add(KeyEvent.KEYCODE_INSERT, LwjglGlfwKeycode.GLFW_KEY_INSERT);
|
||||
|
||||
// Fn keys
|
||||
add(KeyEvent.KEYCODE_F1, LWJGLGLFWKeycode.GLFW_KEY_F1); //131
|
||||
add(KeyEvent.KEYCODE_F2, LWJGLGLFWKeycode.GLFW_KEY_F2);
|
||||
add(KeyEvent.KEYCODE_F3, LWJGLGLFWKeycode.GLFW_KEY_F3);
|
||||
add(KeyEvent.KEYCODE_F4, LWJGLGLFWKeycode.GLFW_KEY_F4);
|
||||
add(KeyEvent.KEYCODE_F5, LWJGLGLFWKeycode.GLFW_KEY_F5);
|
||||
add(KeyEvent.KEYCODE_F6, LWJGLGLFWKeycode.GLFW_KEY_F6);
|
||||
add(KeyEvent.KEYCODE_F7, LWJGLGLFWKeycode.GLFW_KEY_F7);
|
||||
add(KeyEvent.KEYCODE_F8, LWJGLGLFWKeycode.GLFW_KEY_F8);
|
||||
add(KeyEvent.KEYCODE_F9, LWJGLGLFWKeycode.GLFW_KEY_F9);
|
||||
add(KeyEvent.KEYCODE_F10, LWJGLGLFWKeycode.GLFW_KEY_F10);
|
||||
add(KeyEvent.KEYCODE_F11, LWJGLGLFWKeycode.GLFW_KEY_F11);
|
||||
add(KeyEvent.KEYCODE_F12, LWJGLGLFWKeycode.GLFW_KEY_F12); //142
|
||||
add(KeyEvent.KEYCODE_F1, LwjglGlfwKeycode.GLFW_KEY_F1); //131
|
||||
add(KeyEvent.KEYCODE_F2, LwjglGlfwKeycode.GLFW_KEY_F2);
|
||||
add(KeyEvent.KEYCODE_F3, LwjglGlfwKeycode.GLFW_KEY_F3);
|
||||
add(KeyEvent.KEYCODE_F4, LwjglGlfwKeycode.GLFW_KEY_F4);
|
||||
add(KeyEvent.KEYCODE_F5, LwjglGlfwKeycode.GLFW_KEY_F5);
|
||||
add(KeyEvent.KEYCODE_F6, LwjglGlfwKeycode.GLFW_KEY_F6);
|
||||
add(KeyEvent.KEYCODE_F7, LwjglGlfwKeycode.GLFW_KEY_F7);
|
||||
add(KeyEvent.KEYCODE_F8, LwjglGlfwKeycode.GLFW_KEY_F8);
|
||||
add(KeyEvent.KEYCODE_F9, LwjglGlfwKeycode.GLFW_KEY_F9);
|
||||
add(KeyEvent.KEYCODE_F10, LwjglGlfwKeycode.GLFW_KEY_F10);
|
||||
add(KeyEvent.KEYCODE_F11, LwjglGlfwKeycode.GLFW_KEY_F11);
|
||||
add(KeyEvent.KEYCODE_F12, LwjglGlfwKeycode.GLFW_KEY_F12); //142
|
||||
|
||||
// Num keys
|
||||
add(KeyEvent.KEYCODE_NUM_LOCK, LWJGLGLFWKeycode.GLFW_KEY_NUM_LOCK); //143
|
||||
add(KeyEvent.KEYCODE_NUMPAD_0, LWJGLGLFWKeycode.GLFW_KEY_0);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_1, LWJGLGLFWKeycode.GLFW_KEY_1);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_2, LWJGLGLFWKeycode.GLFW_KEY_2);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_3, LWJGLGLFWKeycode.GLFW_KEY_3);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_4, LWJGLGLFWKeycode.GLFW_KEY_4);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_5, LWJGLGLFWKeycode.GLFW_KEY_5);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_6, LWJGLGLFWKeycode.GLFW_KEY_6);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_7, LWJGLGLFWKeycode.GLFW_KEY_7);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_8, LWJGLGLFWKeycode.GLFW_KEY_8);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_9, LWJGLGLFWKeycode.GLFW_KEY_9);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_DIVIDE, LWJGLGLFWKeycode.GLFW_KEY_KP_DIVIDE);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_MULTIPLY, LWJGLGLFWKeycode.GLFW_KEY_KP_MULTIPLY);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_SUBTRACT, LWJGLGLFWKeycode.GLFW_KEY_KP_SUBTRACT);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_ADD, LWJGLGLFWKeycode.GLFW_KEY_KP_ADD);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_DOT, LWJGLGLFWKeycode.GLFW_KEY_PERIOD);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_COMMA, LWJGLGLFWKeycode.GLFW_KEY_COMMA);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_ENTER, LWJGLGLFWKeycode.GLFW_KEY_ENTER);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_EQUALS, LWJGLGLFWKeycode.GLFW_KEY_EQUAL); //161
|
||||
add(KeyEvent.KEYCODE_NUM_LOCK, LwjglGlfwKeycode.GLFW_KEY_NUM_LOCK); //143
|
||||
add(KeyEvent.KEYCODE_NUMPAD_0, LwjglGlfwKeycode.GLFW_KEY_0);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_1, LwjglGlfwKeycode.GLFW_KEY_1);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_2, LwjglGlfwKeycode.GLFW_KEY_2);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_3, LwjglGlfwKeycode.GLFW_KEY_3);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_4, LwjglGlfwKeycode.GLFW_KEY_4);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_5, LwjglGlfwKeycode.GLFW_KEY_5);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_6, LwjglGlfwKeycode.GLFW_KEY_6);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_7, LwjglGlfwKeycode.GLFW_KEY_7);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_8, LwjglGlfwKeycode.GLFW_KEY_8);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_9, LwjglGlfwKeycode.GLFW_KEY_9);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_DIVIDE, LwjglGlfwKeycode.GLFW_KEY_KP_DIVIDE);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_MULTIPLY, LwjglGlfwKeycode.GLFW_KEY_KP_MULTIPLY);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_SUBTRACT, LwjglGlfwKeycode.GLFW_KEY_KP_SUBTRACT);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_ADD, LwjglGlfwKeycode.GLFW_KEY_KP_ADD);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_DOT, LwjglGlfwKeycode.GLFW_KEY_PERIOD);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_COMMA, LwjglGlfwKeycode.GLFW_KEY_COMMA);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_ENTER, LwjglGlfwKeycode.GLFW_KEY_ENTER);
|
||||
add(KeyEvent.KEYCODE_NUMPAD_EQUALS, LwjglGlfwKeycode.GLFW_KEY_EQUAL); //161
|
||||
|
||||
}
|
||||
|
||||
private static short index = 0;
|
||||
|
||||
private static void add(int androidKeycode, short LWJGLKeycode){
|
||||
androidKeycodes[index] = androidKeycode;
|
||||
LWJGLKeycodes[index] = LWJGLKeycode;
|
||||
++index;
|
||||
}
|
||||
|
||||
|
||||
public static boolean containsKey(int keycode){
|
||||
return getIndexByKey(keycode) >= 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public static String[] generateKeyName() {
|
||||
if (androidKeyNameArray == null) {
|
||||
androidKeyNameArray = new String[androidKeycodes.length];
|
||||
androidKeyNameArray = new String[sAndroidKeycodes.length];
|
||||
for(int i=0; i < androidKeyNameArray.length; ++i){
|
||||
androidKeyNameArray[i] = KeyEvent.keyCodeToString(androidKeycodes[i]).replace("KEYCODE_", "");
|
||||
androidKeyNameArray[i] = KeyEvent.keyCodeToString(sAndroidKeycodes[i]).replace("KEYCODE_", "");
|
||||
}
|
||||
}
|
||||
return androidKeyNameArray;
|
||||
|
|
@ -185,7 +175,6 @@ public class EfficientAndroidLWJGLKeycode {
|
|||
execKey(keyEvent, getIndexByKey(keyEvent.getKeyCode()));
|
||||
}
|
||||
|
||||
|
||||
public static void execKey(KeyEvent keyEvent, int valueIndex) {
|
||||
//valueIndex points to where the value is stored in the array.
|
||||
CallbackBridge.holdingAlt = keyEvent.isAltPressed();
|
||||
|
|
@ -194,45 +183,45 @@ public class EfficientAndroidLWJGLKeycode {
|
|||
CallbackBridge.holdingNumlock = keyEvent.isNumLockOn();
|
||||
CallbackBridge.holdingShift = keyEvent.isShiftPressed();
|
||||
|
||||
try {
|
||||
System.out.println(keyEvent.getKeyCode() + " " +keyEvent.getDisplayLabel());
|
||||
char key = (char)(keyEvent.getUnicodeChar() != 0 ? keyEvent.getUnicodeChar() : '\u0000');
|
||||
BaseMainActivity.sendKeyPress(
|
||||
getValueByIndex(valueIndex),
|
||||
key,
|
||||
0,
|
||||
CallbackBridge.getCurrentMods(),
|
||||
keyEvent.getAction() == KeyEvent.ACTION_DOWN);
|
||||
|
||||
} catch (Throwable th) {
|
||||
th.printStackTrace();
|
||||
}
|
||||
System.out.println(keyEvent.getKeyCode() + " " +keyEvent.getDisplayLabel());
|
||||
char key = (char)(keyEvent.getUnicodeChar() != 0 ? keyEvent.getUnicodeChar() : '\u0000');
|
||||
sendKeyPress(
|
||||
getValueByIndex(valueIndex),
|
||||
key,
|
||||
0,
|
||||
CallbackBridge.getCurrentMods(),
|
||||
keyEvent.getAction() == KeyEvent.ACTION_DOWN);
|
||||
}
|
||||
|
||||
public static void execKeyIndex(int index){
|
||||
//Send a quick key press.
|
||||
BaseMainActivity.sendKeyPress(getValueByIndex(index));
|
||||
sendKeyPress(getValueByIndex(index));
|
||||
}
|
||||
|
||||
public static int getValueByIndex(int index) {
|
||||
return LWJGLKeycodes[index];
|
||||
return sLwjglKeycodes[index];
|
||||
}
|
||||
|
||||
public static int getIndexByKey(int key){
|
||||
return Arrays.binarySearch(androidKeycodes, key);
|
||||
return Arrays.binarySearch(sAndroidKeycodes, key);
|
||||
}
|
||||
|
||||
public static short getValue(int key){
|
||||
return LWJGLKeycodes[Arrays.binarySearch(androidKeycodes, key)];
|
||||
return sLwjglKeycodes[Arrays.binarySearch(sAndroidKeycodes, key)];
|
||||
}
|
||||
|
||||
/** @return the index at which the key is in the array, searching linearly */
|
||||
public static int getIndexByValue(int lwjglKey) {
|
||||
//Since the LWJGL keycodes aren't sorted, linear search is used.
|
||||
//You should avoid using this function on performance critical areas
|
||||
for (int i = 0; i < LWJGLKeycodes.length; i++) {
|
||||
if(LWJGLKeycodes[i] == lwjglKey) return i;
|
||||
for (int i = 0; i < sLwjglKeycodes.length; i++) {
|
||||
if(sLwjglKeycodes[i] == lwjglKey) return i;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
private static void add(int androidKeycode, short LWJGLKeycode){
|
||||
sAndroidKeycodes[mTmpCount] = androidKeycode;
|
||||
sLwjglKeycodes[mTmpCount] = LWJGLKeycode;
|
||||
mTmpCount ++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,21 +1,18 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
|
||||
@Keep
|
||||
public class ExitActivity extends AppCompatActivity {
|
||||
public static void showExitMessage(Context ctx, int code) {
|
||||
Intent i = new Intent(ctx,ExitActivity.class);
|
||||
i.putExtra("code",code);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ctx.startActivity(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
@ -24,14 +21,20 @@ public class ExitActivity extends AppCompatActivity {
|
|||
if(extras != null) {
|
||||
code = extras.getInt("code",-1);
|
||||
}
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setMessage(getString(R.string.mcn_exit_title,code))
|
||||
.setPositiveButton(android.R.string.ok,(dialog,which)->{
|
||||
dialog.dismiss();
|
||||
ExitActivity.this.finish();
|
||||
}).setOnCancelListener((z)->{
|
||||
ExitActivity.this.finish();
|
||||
})
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.setOnDismissListener(dialog -> ExitActivity.this.finish())
|
||||
.show();
|
||||
}
|
||||
|
||||
public static void showExitMessage(Context ctx, int code) {
|
||||
Intent i = new Intent(ctx,ExitActivity.class);
|
||||
i.putExtra("code",code);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ctx.startActivity(i);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,70 +1,47 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.os.*;
|
||||
import androidx.appcompat.app.*;
|
||||
import android.util.*;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
public class FatalErrorActivity extends AppCompatActivity
|
||||
{
|
||||
public static void showError(Context ctx, String savePath, boolean storageAllow, /* boolean isFatalErr, */ Throwable th) {
|
||||
Intent ferrorIntent = new Intent(ctx, FatalErrorActivity.class);
|
||||
ferrorIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
ferrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ferrorIntent.putExtra("throwable", th);
|
||||
ferrorIntent.putExtra("savePath", savePath);
|
||||
ferrorIntent.putExtra("storageAllow", storageAllow);
|
||||
// ferrorIntent.putExtra("isFatal", isFatalErr);
|
||||
ctx.startActivity(ferrorIntent);
|
||||
}
|
||||
|
||||
public class FatalErrorActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Bundle extras = getIntent().getExtras();
|
||||
boolean storageAllow = extras.getBoolean("storageAllow");
|
||||
final String strStackTrace = Log.getStackTraceString((Throwable) extras.getSerializable("throwable"));
|
||||
final String stackTrace = Log.getStackTraceString((Throwable) extras.getSerializable("throwable"));
|
||||
String strSavePath = extras.getString("savePath");
|
||||
String errHeader = storageAllow ?
|
||||
"Crash stack trace saved to " + strSavePath + "." :
|
||||
"Storage permission is required to save crash stack trace!";
|
||||
|
||||
// boolean isFatalError = extras.getBoolean("isFatal", false);
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle(R.string.error_fatal)
|
||||
.setMessage(errHeader + "\n\n" + strStackTrace)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener(){
|
||||
.setMessage(errHeader + "\n\n" + stackTrace)
|
||||
.setPositiveButton(android.R.string.ok, (p1, p2) -> finish())
|
||||
.setNegativeButton(R.string.global_restart, (p1, p2) -> startActivity(new Intent(FatalErrorActivity.this, PojavLoginActivity.class)))
|
||||
.setNeutralButton(android.R.string.copy, (p1, p2) -> {
|
||||
ClipboardManager mgr = (ClipboardManager) FatalErrorActivity.this.getSystemService(CLIPBOARD_SERVICE);
|
||||
mgr.setPrimaryClip(ClipData.newPlainText("error", stackTrace));
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface p1, int p2) {
|
||||
finish();
|
||||
}
|
||||
finish();
|
||||
})
|
||||
.setNegativeButton(R.string.global_restart, new DialogInterface.OnClickListener(){
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface p1, int p2) {
|
||||
startActivity(new Intent(FatalErrorActivity.this, PojavLoginActivity.class));
|
||||
}
|
||||
})
|
||||
.setNeutralButton(android.R.string.copy, new DialogInterface.OnClickListener(){
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface p1, int p2) {
|
||||
ClipboardManager mgr = (ClipboardManager) FatalErrorActivity.this.getSystemService(CLIPBOARD_SERVICE);
|
||||
mgr.setPrimaryClip(ClipData.newPlainText("error", strStackTrace));
|
||||
|
||||
finish();
|
||||
}
|
||||
})
|
||||
//.setNegativeButton("Report (not available)", null)
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
|
||||
// Tools.showError(this, isFatalError ? R.string.error_fatal : R.string.global_error, th, true);
|
||||
}
|
||||
|
||||
public static void showError(Context ctx, String savePath, boolean storageAllow, Throwable th) {
|
||||
Intent ferrorIntent = new Intent(ctx, FatalErrorActivity.class);
|
||||
ferrorIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
ferrorIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
ferrorIntent.putExtra("throwable", th);
|
||||
ferrorIntent.putExtra("savePath", savePath);
|
||||
ferrorIntent.putExtra("storageAllow", storageAllow);
|
||||
ctx.startActivity(ferrorIntent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -43,7 +43,7 @@ public class ImportControlActivity extends Activity {
|
|||
super.onCreate(savedInstanceState);
|
||||
Tools.initContextConstants(getApplicationContext());
|
||||
|
||||
setContentView(R.layout.import_control_layout);
|
||||
setContentView(R.layout.activity_import_control);
|
||||
mEditText = findViewById(R.id.editText_import_control_file_name);
|
||||
}
|
||||
|
||||
|
|
@ -185,12 +185,10 @@ public class ImportControlActivity extends Activity {
|
|||
String jsonLayoutData = Tools.read(Tools.CTRLMAP_PATH + "/TMP_IMPORT_FILE.json");
|
||||
JSONObject layoutJobj = new JSONObject(jsonLayoutData);
|
||||
return layoutJobj.has("version") && layoutJobj.has("mControlDataList");
|
||||
|
||||
}catch (JSONException | IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getNameFromURI(Uri uri) {
|
||||
|
|
|
|||
|
|
@ -1,52 +0,0 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
import android.os.*;
|
||||
|
||||
public class InstallerTask extends AsyncTask<String, Void, String>
|
||||
{
|
||||
@Override
|
||||
protected String doInBackground(String[] p1)
|
||||
{
|
||||
try
|
||||
{
|
||||
downloadLibraries(p1[0]);
|
||||
dexMinecraftLibs();
|
||||
downloadMinecraft(p1[0]);
|
||||
dexMinecraftClient(p1[0]);
|
||||
downloadAssets(p1[0]);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return e.getMessage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@Override
|
||||
protected void onPostExecute(String result)
|
||||
{
|
||||
super.onPostExecute(result);
|
||||
|
||||
if(result == null){
|
||||
//No errors
|
||||
}
|
||||
}
|
||||
private void downloadLibraries(String versionName) throws Exception
|
||||
{
|
||||
|
||||
}
|
||||
private void dexMinecraftLibs() throws Exception
|
||||
{
|
||||
|
||||
}
|
||||
private void downloadMinecraft(String versionName) throws Exception
|
||||
{
|
||||
|
||||
}
|
||||
private void dexMinecraftClient(String version) throws Exception
|
||||
{
|
||||
|
||||
}
|
||||
private void downloadAssets(String versionName) throws Exception
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,11 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import com.google.gson.annotations.SerializedName;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public class JAssets {
|
||||
public boolean map_to_resources;
|
||||
@SerializedName("map_to_resources") public boolean mapToResources;
|
||||
public Map<String, JAssetInfo> objects;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,9 +1,10 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import java.util.Map;
|
||||
import net.kdt.pojavlaunch.value.*;
|
||||
import androidx.annotation.Keep;
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.value.*;
|
||||
|
||||
@Keep
|
||||
public class JMinecraftVersionList {
|
||||
public static final String TYPE_OLD_ALPHA = "old_alpha";
|
||||
public static final String TYPE_OLD_BETA = "old_beta";
|
||||
|
|
@ -12,18 +13,24 @@ public class JMinecraftVersionList {
|
|||
public Map<String, String> latest;
|
||||
public Version[] versions;
|
||||
|
||||
public static class Version {
|
||||
@Keep
|
||||
public static class FileProperties {
|
||||
public String id, sha1, url;
|
||||
public long size;
|
||||
}
|
||||
|
||||
@Keep
|
||||
public static class Version extends FileProperties {
|
||||
// Since 1.13, so it's one of ways to check
|
||||
public Arguments arguments;
|
||||
|
||||
public AssetIndex assetIndex;
|
||||
|
||||
public String assets;
|
||||
public Map<String, MinecraftClientInfo> downloads;
|
||||
public String id;
|
||||
public String inheritsFrom;
|
||||
public JavaVersionInfo javaVersion;
|
||||
public DependentLibrary[] libraries;
|
||||
public LoggingConfig logging;
|
||||
public String mainClass;
|
||||
public String minecraftArguments;
|
||||
public int minimumLauncherVersion;
|
||||
|
|
@ -31,25 +38,38 @@ public class JMinecraftVersionList {
|
|||
public String releaseTime;
|
||||
public String time;
|
||||
public String type;
|
||||
public String url;
|
||||
public String sha1;
|
||||
}
|
||||
@Keep
|
||||
public static class JavaVersionInfo {
|
||||
public String component;
|
||||
public int majorVersion;
|
||||
}
|
||||
@Keep
|
||||
public static class LoggingConfig {
|
||||
public LoggingClientConfig client;
|
||||
|
||||
@Keep
|
||||
public static class LoggingClientConfig {
|
||||
public String argument;
|
||||
public FileProperties file;
|
||||
public String type;
|
||||
}
|
||||
}
|
||||
// Since 1.13
|
||||
@Keep
|
||||
public static class Arguments {
|
||||
public Object[] game;
|
||||
public Object[] jvm;
|
||||
|
||||
@Keep
|
||||
public static class ArgValue {
|
||||
public ArgRules[] rules;
|
||||
public String value;
|
||||
|
||||
|
||||
// TLauncher styled argument...
|
||||
public String[] values;
|
||||
|
||||
@Keep
|
||||
public static class ArgRules {
|
||||
public String action;
|
||||
public String features;
|
||||
|
|
@ -62,9 +82,9 @@ public class JMinecraftVersionList {
|
|||
}
|
||||
}
|
||||
}
|
||||
public static class AssetIndex {
|
||||
public String id, sha1, url;
|
||||
public long size, totalSize;
|
||||
@Keep
|
||||
public static class AssetIndex extends FileProperties {
|
||||
public long totalSize;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,220 +1,172 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.graphics.*;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.os.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
import android.view.View.*;
|
||||
import android.widget.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
|
||||
import net.kdt.pojavlaunch.customcontrols.keyboard.AwtCharSender;
|
||||
import net.kdt.pojavlaunch.customcontrols.keyboard.TouchCharInput;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
|
||||
import net.kdt.pojavlaunch.prefs.*;
|
||||
import net.kdt.pojavlaunch.utils.*;
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
import static net.kdt.pojavlaunch.utils.MathUtils.map;
|
||||
import com.kdt.LoggerView;
|
||||
|
||||
public class JavaGUILauncherActivity extends LoggableActivity implements View.OnTouchListener {
|
||||
public class JavaGUILauncherActivity extends BaseActivity implements View.OnTouchListener {
|
||||
private static final int MSG_LEFT_MOUSE_BUTTON_CHECK = 1028;
|
||||
|
||||
private AWTCanvasView mTextureView;
|
||||
private LinearLayout contentLog;
|
||||
private TextView textLog;
|
||||
private ScrollView contentScroll;
|
||||
private ToggleButton toggleLog;
|
||||
private LoggerView mLoggerView;
|
||||
private TouchCharInput mTouchCharInput;
|
||||
|
||||
private LinearLayout touchPad;
|
||||
private ImageView mousePointer;
|
||||
private GestureDetector gestureDetector;
|
||||
|
||||
private File logFile;
|
||||
private PrintStream logStream;
|
||||
|
||||
private final Object mDialogLock = new Object();
|
||||
|
||||
private boolean isLogAllow, mSkipDetectMod;
|
||||
|
||||
private boolean rightOverride = false;
|
||||
private int scaleFactor;
|
||||
private int[] scaleFactors = initScaleFactors();
|
||||
|
||||
private final int fingerStillThreshold = 8;
|
||||
private int initialX;
|
||||
private int initialY;
|
||||
private static boolean triggeredLeftMouseButton = false;
|
||||
private Handler theHandler = new Handler() {
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_LEFT_MOUSE_BUTTON_CHECK: {
|
||||
int x = CallbackBridge.mouseX;
|
||||
int y = CallbackBridge.mouseY;
|
||||
if (CallbackBridge.isGrabbing() &&
|
||||
Math.abs(initialX - x) < fingerStillThreshold &&
|
||||
Math.abs(initialY - y) < fingerStillThreshold) {
|
||||
triggeredLeftMouseButton = true;
|
||||
AWTInputBridge.sendMousePress(AWTInputEvent.BUTTON1_DOWN_MASK, true);
|
||||
}
|
||||
} break;
|
||||
}
|
||||
}
|
||||
};
|
||||
private LinearLayout mTouchPad;
|
||||
private ImageView mMousePointerImageView;
|
||||
private GestureDetector mGestureDetector;
|
||||
|
||||
private boolean mSkipDetectMod, mIsVirtualMouseEnabled;
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.install_mod);
|
||||
setContentView(R.layout.activity_java_gui_launcher);
|
||||
|
||||
Logger.getInstance().reset();
|
||||
mTouchCharInput = findViewById(R.id.awt_touch_char);
|
||||
mTouchCharInput.setCharacterSender(new AwtCharSender());
|
||||
|
||||
mTouchPad = findViewById(R.id.main_touchpad);
|
||||
mLoggerView = findViewById(R.id.launcherLoggerView);
|
||||
mMousePointerImageView = findViewById(R.id.main_mouse_pointer);
|
||||
mTextureView = findViewById(R.id.installmod_surfaceview);
|
||||
mGestureDetector = new GestureDetector(this, new SingleTapConfirm());
|
||||
mTouchPad.setFocusable(false);
|
||||
mTouchPad.setVisibility(View.GONE);
|
||||
|
||||
findViewById(R.id.installmod_mouse_pri).setOnTouchListener(this);
|
||||
findViewById(R.id.installmod_mouse_sec).setOnTouchListener(this);
|
||||
|
||||
mMousePointerImageView.post(() -> {
|
||||
ViewGroup.LayoutParams params = mMousePointerImageView.getLayoutParams();
|
||||
params.width = (int) (36 / 100f * LauncherPreferences.PREF_MOUSESCALE);
|
||||
params.height = (int) (54 / 100f * LauncherPreferences.PREF_MOUSESCALE);
|
||||
});
|
||||
|
||||
mTouchPad.setOnTouchListener(new View.OnTouchListener() {
|
||||
float prevX = 0, prevY = 0;
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
// MotionEvent reports input details from the touch screen
|
||||
// and other input controls. In this case, you are only
|
||||
// interested in events where the touch position changed.
|
||||
// int index = event.getActionIndex();
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
float mouseX, mouseY;
|
||||
|
||||
mouseX = mMousePointerImageView.getX();
|
||||
mouseY = mMousePointerImageView.getY();
|
||||
|
||||
if (mGestureDetector.onTouchEvent(event)) {
|
||||
sendScaledMousePosition(mouseX,mouseY);
|
||||
AWTInputBridge.sendMousePress(AWTInputEvent.BUTTON1_DOWN_MASK);
|
||||
} else {
|
||||
if (action == MotionEvent.ACTION_MOVE) { // 2
|
||||
mouseX = Math.max(0, Math.min(CallbackBridge.physicalWidth, mouseX + x - prevX));
|
||||
mouseY = Math.max(0, Math.min(CallbackBridge.physicalHeight, mouseY + y - prevY));
|
||||
placeMouseAt(mouseX, mouseY);
|
||||
sendScaledMousePosition(mouseX, mouseY);
|
||||
}
|
||||
}
|
||||
|
||||
// debugText.setText(CallbackBridge.DEBUG_STRING.toString());
|
||||
CallbackBridge.DEBUG_STRING.setLength(0);
|
||||
prevY = y;
|
||||
prevX = x;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
mTextureView.setOnTouchListener((v, event) -> {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
if (mGestureDetector.onTouchEvent(event)) {
|
||||
sendScaledMousePosition(x + mTextureView.getX(), y);
|
||||
AWTInputBridge.sendMousePress(AWTInputEvent.BUTTON1_DOWN_MASK);
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_UP: // 1
|
||||
case MotionEvent.ACTION_CANCEL: // 3
|
||||
case MotionEvent.ACTION_POINTER_UP: // 6
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE: // 2
|
||||
sendScaledMousePosition(x + mTextureView.getX(), y);
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
});
|
||||
|
||||
Tools.updateWindowSize(this);
|
||||
|
||||
try {
|
||||
MultiRTUtils.setRuntimeNamed(this,LauncherPreferences.PREF_DEFAULT_RUNTIME);
|
||||
gestureDetector = new GestureDetector(this, new SingleTapConfirm());
|
||||
JREUtils.jreReleaseList = JREUtils.readJREReleaseProperties(LauncherPreferences.PREF_DEFAULT_RUNTIME);
|
||||
if (JREUtils.jreReleaseList.get("JAVA_VERSION").equals("1.8.0")) {
|
||||
MultiRTUtils.setRuntimeNamed(this,LauncherPreferences.PREF_DEFAULT_RUNTIME);
|
||||
} else {
|
||||
MultiRTUtils.setRuntimeNamed(this,MultiRTUtils.getExactJreName(8));
|
||||
JREUtils.jreReleaseList = JREUtils.readJREReleaseProperties();
|
||||
}
|
||||
|
||||
findViewById(R.id.installmod_mouse_pri).setOnTouchListener(this);
|
||||
findViewById(R.id.installmod_mouse_sec).setOnTouchListener(this);
|
||||
|
||||
this.touchPad = findViewById(R.id.main_touchpad);
|
||||
touchPad.setFocusable(false);
|
||||
|
||||
this.mousePointer = findViewById(R.id.main_mouse_pointer);
|
||||
this.mousePointer.post(() -> {
|
||||
ViewGroup.LayoutParams params = mousePointer.getLayoutParams();
|
||||
params.width = (int) (36 / 100f * LauncherPreferences.PREF_MOUSESCALE);
|
||||
params.height = (int) (54 / 100f * LauncherPreferences.PREF_MOUSESCALE);
|
||||
});
|
||||
|
||||
|
||||
touchPad.setOnTouchListener(new OnTouchListener(){
|
||||
private float prevX, prevY;
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent event) {
|
||||
// MotionEvent reports input details from the touch screen
|
||||
// and other input controls. In this case, you are only
|
||||
// interested in events where the touch position changed.
|
||||
// int index = event.getActionIndex();
|
||||
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
if(event.getHistorySize() > 0) {
|
||||
prevX = event.getHistoricalX(0);
|
||||
prevY = event.getHistoricalY(0);
|
||||
}else{
|
||||
prevX = x;
|
||||
prevY = y;
|
||||
}
|
||||
float mouseX = mousePointer.getX();
|
||||
float mouseY = mousePointer.getY();
|
||||
|
||||
if (gestureDetector.onTouchEvent(event)) {
|
||||
|
||||
sendScaledMousePosition(mouseX,mouseY);
|
||||
|
||||
AWTInputBridge.sendMousePress(rightOverride ? AWTInputEvent.BUTTON3_DOWN_MASK : AWTInputEvent.BUTTON1_DOWN_MASK);
|
||||
|
||||
|
||||
} else {
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_UP: // 1
|
||||
case MotionEvent.ACTION_CANCEL: // 3
|
||||
case MotionEvent.ACTION_POINTER_UP: // 6
|
||||
break;
|
||||
case MotionEvent.ACTION_MOVE: // 2
|
||||
mouseX = Math.max(0, Math.min(CallbackBridge.physicalWidth, mouseX + x - prevX));
|
||||
mouseY = Math.max(0, Math.min(CallbackBridge.physicalHeight, mouseY + y - prevY));
|
||||
placeMouseAt(mouseX, mouseY);
|
||||
|
||||
sendScaledMousePosition(mouseX,mouseY);
|
||||
/*
|
||||
if (!CallbackBridge.isGrabbing()) {
|
||||
CallbackBridge.sendMouseKeycode(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT, 0, isLeftMouseDown);
|
||||
CallbackBridge.sendMouseKeycode(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, 0, isRightMouseDown);
|
||||
}
|
||||
*/
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// debugText.setText(CallbackBridge.DEBUG_STRING.toString());
|
||||
CallbackBridge.DEBUG_STRING.setLength(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
placeMouseAt(CallbackBridge.physicalWidth / 2, CallbackBridge.physicalHeight / 2);
|
||||
|
||||
logFile = new File(Tools.DIR_GAME_HOME, "latestlog.txt");
|
||||
logFile.delete();
|
||||
logFile.createNewFile();
|
||||
logStream = new PrintStream(logFile.getAbsolutePath());
|
||||
|
||||
this.contentLog = findViewById(R.id.content_log_layout);
|
||||
this.contentScroll = (ScrollView) findViewById(R.id.content_log_scroll);
|
||||
this.textLog = (TextView) contentScroll.getChildAt(0);
|
||||
this.toggleLog = (ToggleButton) findViewById(R.id.content_log_toggle_log);
|
||||
this.toggleLog.setChecked(false);
|
||||
// this.textLogBehindGL = (TextView) findViewById(R.id.main_log_behind_GL);
|
||||
// this.textLogBehindGL.setTypeface(Typeface.MONOSPACE);
|
||||
this.textLog.setTypeface(Typeface.MONOSPACE);
|
||||
this.toggleLog.setOnCheckedChangeListener(new ToggleButton.OnCheckedChangeListener(){
|
||||
@Override
|
||||
public void onCheckedChanged(CompoundButton button, boolean isChecked) {
|
||||
isLogAllow = isChecked;
|
||||
appendToLog("");
|
||||
}
|
||||
});
|
||||
|
||||
final File modFile = (File) getIntent().getExtras().getSerializable("modFile");
|
||||
final String javaArgs = getIntent().getExtras().getString("javaArgs");
|
||||
|
||||
mTextureView = findViewById(R.id.installmod_surfaceview);
|
||||
|
||||
mSkipDetectMod = getIntent().getExtras().getBoolean("skipDetectMod", false);
|
||||
if (mSkipDetectMod) {
|
||||
new Thread(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
launchJavaRuntime(modFile, javaArgs);
|
||||
}
|
||||
}, "JREMainThread").start();
|
||||
} else {
|
||||
openLogOutput(null);
|
||||
new Thread(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final int exit = doCustomInstall(modFile, javaArgs);
|
||||
appendlnToLog(getString(R.string.toast_optifine_success));
|
||||
if (exit == 0) {
|
||||
runOnUiThread(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
Toast.makeText(JavaGUILauncherActivity.this, R.string.toast_optifine_success, Toast.LENGTH_SHORT).show();
|
||||
MainActivity.fullyExit();
|
||||
}
|
||||
});
|
||||
} /* else {
|
||||
throw new ErrnoException(getString(R.string.glo, exit);
|
||||
} */
|
||||
} catch (Throwable e) {
|
||||
appendlnToLog("Install failed:");
|
||||
appendlnToLog(Log.getStackTraceString(e));
|
||||
Tools.showError(JavaGUILauncherActivity.this, e);
|
||||
}
|
||||
}
|
||||
}, "Installer").start();
|
||||
new Thread(() -> launchJavaRuntime(modFile, javaArgs), "JREMainThread").start();
|
||||
return;
|
||||
}
|
||||
|
||||
// No skip detection
|
||||
openLogOutput(null);
|
||||
new Thread(() -> {
|
||||
try {
|
||||
final int exit = doCustomInstall(modFile, javaArgs);
|
||||
Logger.getInstance().appendToLog(getString(R.string.toast_optifine_success));
|
||||
if (exit != 0) return;
|
||||
runOnUiThread(() -> {
|
||||
Toast.makeText(JavaGUILauncherActivity.this, R.string.toast_optifine_success, Toast.LENGTH_SHORT).show();
|
||||
MainActivity.fullyExit();
|
||||
});
|
||||
|
||||
} catch (Throwable e) {
|
||||
Logger.getInstance().appendToLog("Install failed:");
|
||||
Logger.getInstance().appendToLog(Log.getStackTraceString(e));
|
||||
Tools.showError(JavaGUILauncherActivity.this, e);
|
||||
}
|
||||
}, "Installer").start();
|
||||
} catch (Throwable th) {
|
||||
Tools.showError(this, th, true);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
final View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouch(View v, MotionEvent e) {
|
||||
boolean isDown;
|
||||
|
|
@ -245,18 +197,24 @@ public class JavaGUILauncherActivity extends LoggableActivity implements View.On
|
|||
}
|
||||
|
||||
public void placeMouseAdd(float x, float y) {
|
||||
this.mousePointer.setX(mousePointer.getX() + x);
|
||||
this.mousePointer.setY(mousePointer.getY() + y);
|
||||
mMousePointerImageView.setX(mMousePointerImageView.getX() + x);
|
||||
mMousePointerImageView.setY(mMousePointerImageView.getY() + y);
|
||||
}
|
||||
|
||||
public void placeMouseAt(float x, float y) {
|
||||
this.mousePointer.setX(x);
|
||||
this.mousePointer.setY(y);
|
||||
mMousePointerImageView.setX(x);
|
||||
mMousePointerImageView.setY(y);
|
||||
}
|
||||
|
||||
void sendScaledMousePosition(float x, float y){
|
||||
AWTInputBridge.sendMousePos((int) map(x,0,CallbackBridge.physicalWidth, scaleFactors[0], scaleFactors[2]),
|
||||
(int) map(y,0,CallbackBridge.physicalHeight, scaleFactors[1], scaleFactors[3]));
|
||||
// Clamp positions to the borders of the usable view, then scale them
|
||||
x = androidx.core.math.MathUtils.clamp(x, mTextureView.getX(), mTextureView.getX() + mTextureView.getWidth());
|
||||
y = androidx.core.math.MathUtils.clamp(y, mTextureView.getY(), mTextureView.getY() + mTextureView.getHeight());
|
||||
|
||||
AWTInputBridge.sendMousePos(
|
||||
(int) MathUtils.map(x, mTextureView.getX(), mTextureView.getX() + mTextureView.getWidth(), 0, AWTCanvasView.AWT_CANVAS_WIDTH),
|
||||
(int) MathUtils.map(y, mTextureView.getY(), mTextureView.getY() + mTextureView.getHeight(), 0, AWTCanvasView.AWT_CANVAS_HEIGHT)
|
||||
);
|
||||
}
|
||||
|
||||
public void forceClose(View v) {
|
||||
|
|
@ -264,34 +222,28 @@ public class JavaGUILauncherActivity extends LoggableActivity implements View.On
|
|||
}
|
||||
|
||||
public void openLogOutput(View v) {
|
||||
contentLog.setVisibility(View.VISIBLE);
|
||||
mLoggerView.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
public void closeLogOutput(View view) {
|
||||
if (mSkipDetectMod) {
|
||||
contentLog.setVisibility(View.GONE);
|
||||
mLoggerView.setVisibility(View.GONE);
|
||||
} else {
|
||||
forceClose(null);
|
||||
}
|
||||
}
|
||||
|
||||
private int doCustomInstall(File modFile, String javaArgs) throws IOException {
|
||||
isLogAllow = true;
|
||||
mSkipDetectMod = true;
|
||||
return launchJavaRuntime(modFile, javaArgs);
|
||||
|
||||
public void toggleVirtualMouse(View v) {
|
||||
mIsVirtualMouseEnabled = !mIsVirtualMouseEnabled;
|
||||
mTouchPad.setVisibility(mIsVirtualMouseEnabled ? View.VISIBLE : View.GONE);
|
||||
Toast.makeText(this,
|
||||
mIsVirtualMouseEnabled ? R.string.control_mouseon : R.string.control_mouseoff,
|
||||
Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public int launchJavaRuntime(File modFile, String javaArgs) {
|
||||
JREUtils.redirectAndPrintJRELog(this);
|
||||
JREUtils.redirectAndPrintJRELog();
|
||||
try {
|
||||
jreReleaseList = JREUtils.readJREReleaseProperties();
|
||||
|
||||
// Fail immediately when Java 8 is not selected
|
||||
// TODO: auto override Java 8 if installed
|
||||
if (!jreReleaseList.get("JAVA_VERSION").equals("1.8.0")) {
|
||||
throw new RuntimeException("Cannot use the mod installer. In order to use the mod installer, you need to install Java 8 and specify it in the Preferences menu.");
|
||||
}
|
||||
|
||||
List<String> javaArgList = new ArrayList<String>();
|
||||
|
||||
// Enable Caciocavallo
|
||||
|
|
@ -304,7 +256,7 @@ public class JavaGUILauncherActivity extends LoggableActivity implements View.On
|
|||
javaArgList.add(modFile.getAbsolutePath());
|
||||
}
|
||||
|
||||
appendlnToLog("Info: Java arguments: " + Arrays.toString(javaArgList.toArray(new String[0])));
|
||||
Logger.getInstance().appendToLog("Info: Java arguments: " + Arrays.toString(javaArgList.toArray(new String[0])));
|
||||
|
||||
// Run java on sandbox, non-overrideable.
|
||||
Collections.reverse(javaArgList);
|
||||
|
|
@ -320,64 +272,14 @@ public class JavaGUILauncherActivity extends LoggableActivity implements View.On
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
final int uiOptions = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION;
|
||||
final View decorView = getWindow().getDecorView();
|
||||
decorView.setSystemUiVisibility(uiOptions);
|
||||
|
||||
|
||||
private int doCustomInstall(File modFile, String javaArgs) throws IOException {
|
||||
mSkipDetectMod = true;
|
||||
return launchJavaRuntime(modFile, javaArgs);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void appendToLog(final String text, boolean checkAllow) {
|
||||
logStream.print(text);
|
||||
if (checkAllow && !isLogAllow) return;
|
||||
textLog.post(() -> {
|
||||
textLog.append(text);
|
||||
contentScroll.fullScroll(ScrollView.FOCUS_DOWN);
|
||||
});
|
||||
}
|
||||
|
||||
int[] initScaleFactors(){
|
||||
return initScaleFactors(true);
|
||||
}
|
||||
|
||||
int[] initScaleFactors(boolean autoScale){
|
||||
//Could be optimized
|
||||
|
||||
if(autoScale) { //Auto scale
|
||||
int minDimension = Math.min(CallbackBridge.physicalHeight, CallbackBridge.physicalWidth);
|
||||
scaleFactor = Math.max(((3 * minDimension) / 1080) - 1, 1);
|
||||
}
|
||||
|
||||
int[] scales = new int[4]; //Left, Top, Right, Bottom
|
||||
|
||||
scales[0] = (CallbackBridge.physicalWidth/2);
|
||||
scales[0] -= scales[0]/scaleFactor;
|
||||
|
||||
scales[1] = (CallbackBridge.physicalHeight/2);
|
||||
scales[1] -= scales[1]/scaleFactor;
|
||||
|
||||
scales[2] = (CallbackBridge.physicalWidth/2);
|
||||
scales[2] += scales[2]/scaleFactor;
|
||||
|
||||
scales[3] = (CallbackBridge.physicalHeight/2);
|
||||
scales[3] += scales[3]/scaleFactor;
|
||||
|
||||
return scales;
|
||||
}
|
||||
|
||||
public void scaleDown(View view) {
|
||||
scaleFactor = Math.max(scaleFactor - 1, 1);
|
||||
scaleFactors = initScaleFactors(false);
|
||||
mTextureView.initScaleFactors(scaleFactor);
|
||||
sendScaledMousePosition(mousePointer.getX(),mousePointer.getY());
|
||||
}
|
||||
|
||||
public void scaleUp(View view) {
|
||||
scaleFactor = Math.min(scaleFactor + 1, 6);
|
||||
scaleFactors = initScaleFactors(false);
|
||||
mTextureView.initScaleFactors(scaleFactor);
|
||||
sendScaledMousePosition(mousePointer.getX(),mousePointer.getY());
|
||||
public void toggleKeyboard(View view) {
|
||||
mTouchCharInput.switchKeyboardState();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,27 +0,0 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
public abstract class LoggableActivity extends BaseActivity {
|
||||
public Map<String, String> jreReleaseList;
|
||||
private boolean filteredSessionID = false;
|
||||
public void appendToLog(String text) {
|
||||
appendToLog(text, true);
|
||||
}
|
||||
|
||||
public void appendlnToLog(String text) {
|
||||
// Filter out Session ID here
|
||||
int index;
|
||||
if (!filteredSessionID && (index = text.indexOf("(Session ID is ")) != -1) {
|
||||
text = text.substring(0, index) + "(Session ID is <censored>)";
|
||||
filteredSessionID = true;
|
||||
}
|
||||
|
||||
appendlnToLog(text, true);
|
||||
}
|
||||
|
||||
public void appendlnToLog(String text, boolean checkAllow) {
|
||||
appendToLog(text + "\n", checkAllow);
|
||||
}
|
||||
public abstract void appendToLog(final String text, boolean checkAllow);
|
||||
}
|
||||
107
app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Logger.java
Normal file
107
app_pojavlauncher/src/main/java/net/kdt/pojavlaunch/Logger.java
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintStream;
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/** Singleton class made to log on one file
|
||||
* The singleton part can be removed but will require more implementation from the end-dev
|
||||
*/
|
||||
@Keep
|
||||
public class Logger {
|
||||
private static Logger sLoggerSingleton = null;
|
||||
|
||||
/* Instance variables */
|
||||
private final File mLogFile;
|
||||
private PrintStream mLogStream;
|
||||
private WeakReference<eventLogListener> mLogListenerWeakReference = null;
|
||||
|
||||
/* No public construction */
|
||||
private Logger(){
|
||||
this("latestlog.txt");
|
||||
}
|
||||
|
||||
private Logger(String fileName){
|
||||
mLogFile = new File(Tools.DIR_GAME_HOME, fileName);
|
||||
// Make a new instance of the log file
|
||||
mLogFile.delete();
|
||||
try {
|
||||
mLogFile.createNewFile();
|
||||
mLogStream = new PrintStream(mLogFile.getAbsolutePath());
|
||||
}catch (IOException e){e.printStackTrace();}
|
||||
|
||||
}
|
||||
|
||||
public static Logger getInstance(){
|
||||
if(sLoggerSingleton == null){
|
||||
synchronized(Logger.class){
|
||||
if(sLoggerSingleton == null){
|
||||
sLoggerSingleton = new Logger();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sLoggerSingleton;
|
||||
}
|
||||
|
||||
|
||||
/** Print the text to the log file if not censored */
|
||||
public void appendToLog(String text){
|
||||
if(shouldCensorLog(text)) return;
|
||||
appendToLogUnchecked(text);
|
||||
}
|
||||
|
||||
/** Print the text to the log file, no china censoring there */
|
||||
public void appendToLogUnchecked(String text){
|
||||
mLogStream.println(text);
|
||||
notifyLogListener(text);
|
||||
}
|
||||
|
||||
/** Reset the log file, effectively erasing any previous logs */
|
||||
public void reset(){
|
||||
try{
|
||||
mLogFile.delete();
|
||||
mLogFile.createNewFile();
|
||||
mLogStream = new PrintStream(mLogFile.getAbsolutePath());
|
||||
}catch (IOException e){ e.printStackTrace();}
|
||||
}
|
||||
|
||||
/** Disables the printing */
|
||||
public void shutdown(){
|
||||
mLogStream.close();
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform various checks to see if the log is safe to print
|
||||
* Subclasses may want to override this behavior
|
||||
* @param text The text to check
|
||||
* @return Whether the log should be censored
|
||||
*/
|
||||
private static boolean shouldCensorLog(String text){
|
||||
if(text.contains("Session ID is")) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Small listener for anything listening to the log */
|
||||
public interface eventLogListener {
|
||||
void onEventLogged(String text);
|
||||
}
|
||||
|
||||
/** Link a log listener to the logger */
|
||||
public void setLogListener(eventLogListener logListener){
|
||||
this.mLogListenerWeakReference = new WeakReference<>(logListener);
|
||||
}
|
||||
|
||||
/** Notifies the event listener, if it exists */
|
||||
private void notifyLogListener(String text){
|
||||
if(mLogListenerWeakReference == null) return;
|
||||
eventLogListener logListener = mLogListenerWeakReference.get();
|
||||
if(logListener == null){
|
||||
mLogListenerWeakReference = null;
|
||||
return;
|
||||
}
|
||||
logListener.onEventLogged(text);
|
||||
}
|
||||
}
|
||||
|
|
@ -30,8 +30,7 @@
|
|||
|
||||
package net.kdt.pojavlaunch;
|
||||
|
||||
public class LWJGLGLFWKeycode
|
||||
{
|
||||
public class LwjglGlfwKeycode {
|
||||
/** The unknown key. */
|
||||
public static final short GLFW_KEY_UNKNOWN = 0; // should be -1
|
||||
|
||||
|
|
@ -195,5 +194,8 @@ public class LWJGLGLFWKeycode
|
|||
GLFW_MOUSE_BUTTON_LEFT = GLFW_MOUSE_BUTTON_1,
|
||||
GLFW_MOUSE_BUTTON_RIGHT = GLFW_MOUSE_BUTTON_2,
|
||||
GLFW_MOUSE_BUTTON_MIDDLE = GLFW_MOUSE_BUTTON_3;
|
||||
|
||||
|
||||
public static final int
|
||||
GLFW_VISIBLE = 0x20004,
|
||||
GLFW_HOVERED = 0x2000B;
|
||||
}
|
||||
|
|
@ -3,10 +3,11 @@ package net.kdt.pojavlaunch;
|
|||
import android.app.Activity;
|
||||
import android.content.Intent;
|
||||
import android.os.*;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.kdt.pojavlaunch.customcontrols.*;
|
||||
import net.kdt.pojavlaunch.prefs.*;
|
||||
import net.kdt.pojavlaunch.utils.MCOptionUtils;
|
||||
|
|
@ -14,58 +15,45 @@ import net.kdt.pojavlaunch.utils.MCOptionUtils;
|
|||
import java.io.*;
|
||||
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_SUSTAINED_PERFORMANCE;
|
||||
|
||||
public class MainActivity extends BaseMainActivity {
|
||||
public static ControlLayout mControlLayout;
|
||||
|
||||
private FileObserver fileObserver;
|
||||
private MCOptionUtils.MCOptionListener optionListener;
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
initLayout(R.layout.main_with_customctrl);
|
||||
super.ingameControlsEditorListener = menuItem -> {
|
||||
switch (menuItem.getItemId()) {
|
||||
case R.id.menu_ctrl_load:
|
||||
CustomControlsActivity.load(mControlLayout);
|
||||
break;
|
||||
case R.id.menu_ctrl_add:
|
||||
|
||||
initLayout(R.layout.activity_basemain);
|
||||
|
||||
// Set the sustained performance mode for available APIs
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
|
||||
getWindow().setSustainedPerformanceMode(PREF_SUSTAINED_PERFORMANCE);
|
||||
ingameControlsEditorArrayAdapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, getResources().getStringArray(R.array.menu_customcontrol));
|
||||
ingameControlsEditorListener = (parent, view, position, id) -> {
|
||||
switch(position) {
|
||||
case 0:
|
||||
mControlLayout.addControlButton(new ControlData("New"));
|
||||
break;
|
||||
case R.id.menu_ctrl_add_drawer:
|
||||
case 1:
|
||||
mControlLayout.addDrawer(new ControlDrawerData());
|
||||
break;
|
||||
case R.id.menu_ctrl_selectdefault:
|
||||
CustomControlsActivity.dialogSelectDefaultCtrl(mControlLayout);
|
||||
case 2:
|
||||
CustomControlsActivity.load(mControlLayout);
|
||||
break;
|
||||
case R.id.menu_ctrl_save:
|
||||
case 3:
|
||||
CustomControlsActivity.save(true,mControlLayout);
|
||||
break;
|
||||
case 4:
|
||||
CustomControlsActivity.dialogSelectDefaultCtrl(mControlLayout);
|
||||
break;
|
||||
}
|
||||
//Toast.makeText(MainActivity.this, menuItem.getTitle() + ":" + menuItem.getItemId(), Toast.LENGTH_SHORT).show();
|
||||
return true;
|
||||
};
|
||||
|
||||
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
|
||||
fileObserver = new FileObserver(new File(Tools.DIR_GAME_NEW + "/options.txt"), FileObserver.MODIFY) {
|
||||
@Override
|
||||
public void onEvent(int i, @Nullable String s) {
|
||||
MCOptionUtils.load();
|
||||
getMcScale();
|
||||
}
|
||||
};
|
||||
}else{
|
||||
fileObserver = new FileObserver(Tools.DIR_GAME_NEW + "/options.txt", FileObserver.MODIFY) {
|
||||
@Override
|
||||
public void onEvent(int i, @Nullable String s) {
|
||||
MCOptionUtils.load();
|
||||
getMcScale();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
fileObserver.startWatching();
|
||||
// Recompute the gui scale when options are changed
|
||||
optionListener = MCOptionUtils::getMcScale;
|
||||
MCOptionUtils.addMCOptionListener(optionListener);
|
||||
|
||||
mControlLayout = findViewById(R.id.main_control_layout);
|
||||
mControlLayout.setModifiable(false);
|
||||
|
|
|
|||
|
|
@ -1,87 +0,0 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.content.Context;
|
||||
import android.text.InputFilter;
|
||||
import android.text.InputFilter.LengthFilter;
|
||||
import android.text.Spanned;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.EditorInfo;
|
||||
import android.view.inputmethod.InputConnection;
|
||||
import android.view.inputmethod.InputConnectionWrapper;
|
||||
import android.widget.EditText;
|
||||
|
||||
public class MainEditText extends EditText {
|
||||
public MainKeyWatcher _MainKeyWatcher;
|
||||
public final int allowedLength;
|
||||
public final boolean limitInput;
|
||||
|
||||
private class MainInputConnection extends InputConnectionWrapper {
|
||||
MainEditText textbox;
|
||||
|
||||
public MainInputConnection(InputConnection target, boolean mutable, MainEditText textbox2) {
|
||||
super(target, mutable);
|
||||
this.textbox = textbox2;
|
||||
}
|
||||
|
||||
public boolean sendKeyEvent(KeyEvent event) {
|
||||
if (this.textbox.getText().length() != 0 || event.getAction() != 0 || event.getKeyCode() != 67) {
|
||||
return super.sendKeyEvent(event);
|
||||
}
|
||||
if (MainEditText.this._MainKeyWatcher != null) {
|
||||
MainEditText.this._MainKeyWatcher.onDeleteKeyPressed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public interface MainKeyWatcher {
|
||||
void onBackKeyPressed();
|
||||
|
||||
void onDeleteKeyPressed();
|
||||
}
|
||||
|
||||
public MainEditText(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
this._MainKeyWatcher = null;
|
||||
this.allowedLength = 160;
|
||||
this.limitInput = false;
|
||||
}
|
||||
|
||||
public MainEditText(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this._MainKeyWatcher = null;
|
||||
this.allowedLength = 160;
|
||||
this.limitInput = false;
|
||||
}
|
||||
|
||||
public MainEditText(Context context, int allowedLength2, boolean limitInput2) {
|
||||
super(context);
|
||||
this._MainKeyWatcher = null;
|
||||
this.allowedLength = allowedLength2;
|
||||
this.limitInput = limitInput2;
|
||||
setFilters(limitInput2 ? new InputFilter[]{new LengthFilter(this.allowedLength), new InputFilter() {
|
||||
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
|
||||
return (!source.equals("") && !source.toString().matches("^[a-zA-Z0-9_ -^~'.,;!#&()=`{}]*")) ? "" : source;
|
||||
}
|
||||
}} : new InputFilter[]{new LengthFilter(this.allowedLength)});
|
||||
}
|
||||
|
||||
public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
|
||||
return new MainInputConnection(super.onCreateInputConnection(outAttrs), true, this);
|
||||
}
|
||||
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
if (keyCode != KeyEvent.KEYCODE_BACK || event.getAction() != KeyEvent.ACTION_UP) {
|
||||
return super.dispatchKeyEvent(event);
|
||||
}
|
||||
if (this._MainKeyWatcher != null) {
|
||||
this._MainKeyWatcher.onBackKeyPressed();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void setOnMainKeyWatcher(MainKeyWatcher MainKeyWatcher) {
|
||||
this._MainKeyWatcher = MainKeyWatcher;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,649 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static net.kdt.pojavlaunch.BaseMainActivity.touchCharInput;
|
||||
import static net.kdt.pojavlaunch.utils.MCOptionUtils.getMcScale;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendMouseButton;
|
||||
import static org.lwjgl.glfw.CallbackBridge.windowHeight;
|
||||
import static org.lwjgl.glfw.CallbackBridge.windowWidth;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.Context;
|
||||
import android.graphics.SurfaceTexture;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.SurfaceHolder;
|
||||
import android.view.SurfaceView;
|
||||
import android.view.TextureView;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import net.kdt.pojavlaunch.utils.MathUtils;
|
||||
|
||||
import net.kdt.pojavlaunch.customcontrols.gamepad.Gamepad;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.utils.JREUtils;
|
||||
import net.kdt.pojavlaunch.utils.MCOptionUtils;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
/**
|
||||
* Class dealing with showing minecraft surface and taking inputs to dispatch them to minecraft
|
||||
*/
|
||||
public class MinecraftGLSurface extends View {
|
||||
/* Gamepad object for gamepad inputs, instantiated on need */
|
||||
private Gamepad mGamepad = null;
|
||||
/* Pointer Debug textview, used to show info about the pointer state */
|
||||
private TextView mPointerDebugTextView;
|
||||
/* Resolution scaler option, allow downsizing a window */
|
||||
private final float mScaleFactor = LauncherPreferences.PREF_SCALE_FACTOR/100f;
|
||||
/* Sensitivity, adjusted according to screen size */
|
||||
private final double mSensitivityFactor = (1.4 * (1080f/ Tools.getDisplayMetrics((Activity) getContext()).heightPixels));
|
||||
/* Use to detect simple and double taps */
|
||||
private final TapDetector mSingleTapDetector = new TapDetector(1, TapDetector.DETECTION_METHOD_BOTH);
|
||||
private final TapDetector mDoubleTapDetector = new TapDetector(2, TapDetector.DETECTION_METHOD_DOWN);
|
||||
/* MC GUI scale, listened by MCOptionUtils */
|
||||
private int mGuiScale = getMcScale();
|
||||
private final MCOptionUtils.MCOptionListener mGuiScaleListener = () -> mGuiScale = getMcScale();
|
||||
/* Surface ready listener, used by the activity to launch minecraft */
|
||||
SurfaceReadyListener mSurfaceReadyListener = null;
|
||||
/* View holding the surface, either a SurfaceView or a TextureView */
|
||||
View mSurface;
|
||||
|
||||
/* List of hotbarKeys, used when clicking on the hotbar */
|
||||
private static final int[] HOTBAR_KEYS = {
|
||||
LwjglGlfwKeycode.GLFW_KEY_1, LwjglGlfwKeycode.GLFW_KEY_2, LwjglGlfwKeycode.GLFW_KEY_3,
|
||||
LwjglGlfwKeycode.GLFW_KEY_4, LwjglGlfwKeycode.GLFW_KEY_5, LwjglGlfwKeycode.GLFW_KEY_6,
|
||||
LwjglGlfwKeycode.GLFW_KEY_7, LwjglGlfwKeycode.GLFW_KEY_8, LwjglGlfwKeycode.GLFW_KEY_9};
|
||||
/* Last hotbar button (0-9) registered */
|
||||
private int mLastHotbarKey = -1;
|
||||
/* Events can start with only a move instead of an pointerDown due to mouse passthrough */
|
||||
private boolean mShouldBeDown = false;
|
||||
/* When fingers are really near to each other, it tends to either swap or remove a pointer ! */
|
||||
private int mLastPointerCount = 0;
|
||||
/* Previous MotionEvent position, not scale */
|
||||
private float mPrevX, mPrevY;
|
||||
/* PointerID used for the moving camera */
|
||||
private int mCurrentPointerID = -1000;
|
||||
/* Initial first pointer positions non-scaled, used to test touch sloppiness */
|
||||
private float mInitialX, mInitialY;
|
||||
/* Last first pointer positions non-scaled, used to scroll distance */
|
||||
private float mScrollLastInitialX, mScrollLastInitialY;
|
||||
/* How much distance a finger has to go for touch sloppiness to be disabled */
|
||||
public static final int FINGER_STILL_THRESHOLD = (int) Tools.dpToPx(9);
|
||||
/* How much distance a finger has to go to scroll */
|
||||
public static final int FINGER_SCROLL_THRESHOLD = (int) Tools.dpToPx(6);
|
||||
/* Whether the button was triggered, used by the handler */
|
||||
private static boolean triggeredLeftMouseButton = false;
|
||||
/* Handle hotbar throw button and mouse mining button */
|
||||
public static final int MSG_LEFT_MOUSE_BUTTON_CHECK = 1028;
|
||||
public static final int MSG_DROP_ITEM_BUTTON_CHECK = 1029;
|
||||
private final Handler mHandler = new Handler(Looper.getMainLooper()) {
|
||||
public void handleMessage(Message msg) {
|
||||
if(msg.what == MSG_LEFT_MOUSE_BUTTON_CHECK) {
|
||||
if (LauncherPreferences.PREF_DISABLE_GESTURES) return;
|
||||
float x = CallbackBridge.mouseX;
|
||||
float y = CallbackBridge.mouseY;
|
||||
if (CallbackBridge.isGrabbing() &&
|
||||
MathUtils.dist(x, y, mInitialX, mInitialY) < FINGER_STILL_THRESHOLD) {
|
||||
triggeredLeftMouseButton = true;
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT, true);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if(msg.what == MSG_DROP_ITEM_BUTTON_CHECK) {
|
||||
if(CallbackBridge.isGrabbing()){
|
||||
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_Q);
|
||||
mHandler.sendEmptyMessageDelayed(MSG_DROP_ITEM_BUTTON_CHECK, 600);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
public MinecraftGLSurface(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MinecraftGLSurface(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
setFocusable(true);
|
||||
|
||||
MCOptionUtils.addMCOptionListener(mGuiScaleListener);
|
||||
}
|
||||
|
||||
/** Initialize the view and all its settings */
|
||||
public void start(){
|
||||
// Add the pointer debug textview
|
||||
mPointerDebugTextView = new TextView(getContext());
|
||||
mPointerDebugTextView.setX(0);
|
||||
mPointerDebugTextView.setY(0);
|
||||
mPointerDebugTextView.setVisibility(GONE);
|
||||
((ViewGroup)getParent()).addView(mPointerDebugTextView);
|
||||
|
||||
if(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE){
|
||||
SurfaceView surfaceView = new SurfaceView(getContext());
|
||||
mSurface = surfaceView;
|
||||
|
||||
surfaceView.getHolder().addCallback(new SurfaceHolder.Callback() {
|
||||
private boolean isCalled = false;
|
||||
@Override
|
||||
public void surfaceCreated(@NonNull SurfaceHolder holder) {
|
||||
if(isCalled) return;
|
||||
isCalled = true;
|
||||
|
||||
realStart(surfaceView.getHolder().getSurface());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceChanged(@NonNull SurfaceHolder holder, int format, int width, int height) {
|
||||
refreshSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void surfaceDestroyed(@NonNull SurfaceHolder holder) {}
|
||||
});
|
||||
|
||||
((ViewGroup)getParent()).addView(surfaceView);
|
||||
}else{
|
||||
TextureView textureView = new TextureView(getContext());
|
||||
textureView.setOpaque(true);
|
||||
mSurface = textureView;
|
||||
|
||||
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {
|
||||
private boolean isCalled = false;
|
||||
@Override
|
||||
public void onSurfaceTextureAvailable(@NonNull SurfaceTexture surface, int width, int height) {
|
||||
if(isCalled) return;
|
||||
isCalled = true;
|
||||
|
||||
realStart(new Surface(textureView.getSurfaceTexture()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureSizeChanged(@NonNull SurfaceTexture surface, int width, int height) {
|
||||
refreshSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onSurfaceTextureDestroyed(@NonNull SurfaceTexture surface) {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSurfaceTextureUpdated(@NonNull SurfaceTexture surface) {}
|
||||
});
|
||||
|
||||
((ViewGroup)getParent()).addView(textureView);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The touch event for both grabbed an non-grabbed mouse state on the touch screen
|
||||
* Does not cover the virtual mouse touchpad
|
||||
*/
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent e) {
|
||||
// Looking for a mouse to handle, won't have an effect if no mouse exists.
|
||||
for (int i = 0; i < e.getPointerCount(); i++) {
|
||||
if (e.getToolType(i) != MotionEvent.TOOL_TYPE_MOUSE) continue;
|
||||
|
||||
// Mouse found
|
||||
if(CallbackBridge.isGrabbing()) return false;
|
||||
CallbackBridge.sendCursorPos( e.getX(i) * mScaleFactor, e.getY(i) * mScaleFactor);
|
||||
return true; //mouse event handled successfully
|
||||
}
|
||||
|
||||
// System.out.println("Pre touch, isTouchInHotbar=" + Boolean.toString(isTouchInHotbar) + ", action=" + MotionEvent.actionToString(e.getActionMasked()));
|
||||
|
||||
//Getting scaled position from the event
|
||||
/* Tells if a double tap happened [MOUSE GRAB ONLY]. Doesn't tell where though. */
|
||||
if(!CallbackBridge.isGrabbing()) {
|
||||
CallbackBridge.mouseX = (e.getX() * mScaleFactor);
|
||||
CallbackBridge.mouseY = (e.getY() * mScaleFactor);
|
||||
//One android click = one MC click
|
||||
if(mSingleTapDetector.onTouchEvent(e)){
|
||||
CallbackBridge.putMouseEventWithCoords(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT, CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Check double tap state, used for the hotbar
|
||||
boolean hasDoubleTapped = mDoubleTapDetector.onTouchEvent(e);
|
||||
|
||||
switch (e.getActionMasked()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
int pointerCount = e.getPointerCount();
|
||||
|
||||
// In-menu interactions
|
||||
if(!CallbackBridge.isGrabbing()){
|
||||
|
||||
// Touch hover
|
||||
if(pointerCount == 1){
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
mPrevX = e.getX();
|
||||
mPrevY = e.getY();
|
||||
break;
|
||||
}
|
||||
|
||||
// Scrolling feature
|
||||
if(LauncherPreferences.PREF_DISABLE_GESTURES) break;
|
||||
// The pointer count can never be 0, and it is not 1, therefore it is >= 2
|
||||
int hScroll = ((int) (e.getX() - mScrollLastInitialX)) / FINGER_SCROLL_THRESHOLD;
|
||||
int vScroll = ((int) (e.getY() - mScrollLastInitialY)) / FINGER_SCROLL_THRESHOLD;
|
||||
|
||||
if(vScroll != 0 || hScroll != 0){
|
||||
CallbackBridge.sendScroll(hScroll, vScroll);
|
||||
mScrollLastInitialX = e.getX();
|
||||
mScrollLastInitialY = e.getY();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Camera movement
|
||||
int pointerIndex = e.findPointerIndex(mCurrentPointerID);
|
||||
int hudKeyHandled = handleGuiBar((int)e.getX(), (int) e.getY());
|
||||
// Start movement, due to new pointer or loss of pointer
|
||||
if (pointerIndex == -1 || mLastPointerCount != pointerCount || !mShouldBeDown) {
|
||||
if(hudKeyHandled != -1) break; //No pointer attribution on hotbar
|
||||
|
||||
mShouldBeDown = true;
|
||||
mCurrentPointerID = e.getPointerId(0);
|
||||
mPrevX = e.getX();
|
||||
mPrevY = e.getY();
|
||||
break;
|
||||
}
|
||||
// Continue movement as usual
|
||||
if(hudKeyHandled == -1){ //No camera on hotbar
|
||||
CallbackBridge.mouseX += (e.getX(pointerIndex) - mPrevX) * mSensitivityFactor;
|
||||
CallbackBridge.mouseY += (e.getY(pointerIndex) - mPrevY) * mSensitivityFactor;
|
||||
}
|
||||
|
||||
mPrevX = e.getX(pointerIndex);
|
||||
mPrevY = e.getY(pointerIndex);
|
||||
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_DOWN: // 0
|
||||
CallbackBridge.sendPrepareGrabInitialPos();
|
||||
|
||||
hudKeyHandled = handleGuiBar((int)e.getX(), (int) e.getY());
|
||||
boolean isTouchInHotbar = hudKeyHandled != -1;
|
||||
if (isTouchInHotbar) {
|
||||
sendKeyPress(hudKeyHandled);
|
||||
if(hasDoubleTapped && hudKeyHandled == mLastHotbarKey){
|
||||
//Prevent double tapping Event on two different slots
|
||||
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_F);
|
||||
}
|
||||
|
||||
mHandler.sendEmptyMessageDelayed(MSG_DROP_ITEM_BUTTON_CHECK, 350);
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
mLastHotbarKey = hudKeyHandled;
|
||||
break;
|
||||
}
|
||||
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
mPrevX = e.getX();
|
||||
mPrevY = e.getY();
|
||||
|
||||
if (CallbackBridge.isGrabbing()) {
|
||||
mCurrentPointerID = e.getPointerId(0);
|
||||
// It cause hold left mouse while moving camera
|
||||
mInitialX = CallbackBridge.mouseX;
|
||||
mInitialY = CallbackBridge.mouseY;
|
||||
mHandler.sendEmptyMessageDelayed(MSG_LEFT_MOUSE_BUTTON_CHECK, LauncherPreferences.PREF_LONGPRESS_TRIGGER);
|
||||
}
|
||||
mLastHotbarKey = hudKeyHandled;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP: // 1
|
||||
case MotionEvent.ACTION_CANCEL: // 3
|
||||
mShouldBeDown = false;
|
||||
mCurrentPointerID = -1;
|
||||
|
||||
hudKeyHandled = handleGuiBar((int)e.getX(), (int) e.getY());
|
||||
isTouchInHotbar = hudKeyHandled != -1;
|
||||
// We only treat in world events
|
||||
if (!CallbackBridge.isGrabbing()) break;
|
||||
|
||||
// Stop the dropping of items
|
||||
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_Q, 0, false);
|
||||
mHandler.removeMessages(MSG_DROP_ITEM_BUTTON_CHECK);
|
||||
|
||||
// Remove the mouse left button
|
||||
if(triggeredLeftMouseButton){
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT, false);
|
||||
triggeredLeftMouseButton = false;
|
||||
break;
|
||||
}
|
||||
mHandler.removeMessages(MSG_LEFT_MOUSE_BUTTON_CHECK);
|
||||
|
||||
// In case of a short click, just send a quick right click
|
||||
if(!LauncherPreferences.PREF_DISABLE_GESTURES &&
|
||||
MathUtils.dist(mInitialX, mInitialY, CallbackBridge.mouseX, CallbackBridge.mouseY) < FINGER_STILL_THRESHOLD){
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, true);
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, false);
|
||||
}
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_POINTER_DOWN: // 5
|
||||
//TODO Hey we could have some sort of middle click detection ?
|
||||
|
||||
mScrollLastInitialX = e.getX();
|
||||
mScrollLastInitialY = e.getY();
|
||||
//Checking if we are pressing the hotbar to select the item
|
||||
hudKeyHandled = handleGuiBar((int)e.getX(e.getPointerCount()-1), (int) e.getY(e.getPointerCount()-1));
|
||||
if(hudKeyHandled != -1){
|
||||
sendKeyPress(hudKeyHandled);
|
||||
if(hasDoubleTapped && hudKeyHandled == mLastHotbarKey){
|
||||
//Prevent double tapping Event on two different slots
|
||||
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_F);
|
||||
}
|
||||
}
|
||||
|
||||
mLastHotbarKey = hudKeyHandled;
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
// Actualise the pointer count
|
||||
mLastPointerCount = e.getPointerCount();
|
||||
|
||||
//debugText.setText(CallbackBridge.DEBUG_STRING.toString());
|
||||
CallbackBridge.DEBUG_STRING.setLength(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The event for mouse/joystick movements
|
||||
* We don't do the gamepad right now.
|
||||
*/
|
||||
@Override
|
||||
public boolean dispatchGenericMotionEvent(MotionEvent event) {
|
||||
int mouseCursorIndex = -1;
|
||||
|
||||
if(Gamepad.isGamepadEvent(event)){
|
||||
if(mGamepad == null){
|
||||
mGamepad = new Gamepad(this, event.getDevice());
|
||||
}
|
||||
|
||||
mGamepad.update(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
for(int i = 0; i < event.getPointerCount(); i++) {
|
||||
if(event.getToolType(i) != MotionEvent.TOOL_TYPE_MOUSE) continue;
|
||||
// Mouse found
|
||||
mouseCursorIndex = i;
|
||||
break;
|
||||
}
|
||||
if(mouseCursorIndex == -1) return false; // we cant consoom that, theres no mice!
|
||||
if(CallbackBridge.isGrabbing()) {
|
||||
if(BaseMainActivity.isAndroid8OrHigher()){
|
||||
requestFocus();
|
||||
requestPointerCapture();
|
||||
}
|
||||
}
|
||||
switch(event.getActionMasked()) {
|
||||
case MotionEvent.ACTION_HOVER_MOVE:
|
||||
CallbackBridge.mouseX = (event.getX(mouseCursorIndex) * mScaleFactor);
|
||||
CallbackBridge.mouseY = (event.getY(mouseCursorIndex) * mScaleFactor);
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
//debugText.setText(CallbackBridge.DEBUG_STRING.toString());
|
||||
CallbackBridge.DEBUG_STRING.setLength(0);
|
||||
return true;
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
CallbackBridge.sendScroll((double) event.getAxisValue(MotionEvent.AXIS_VSCROLL), (double) event.getAxisValue(MotionEvent.AXIS_HSCROLL));
|
||||
return true;
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
return sendMouseButtonUnconverted(event.getActionButton(),true);
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
return sendMouseButtonUnconverted(event.getActionButton(),false);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//TODO MOVE THIS SOMEWHERE ELSE
|
||||
private boolean debugErrored = false;
|
||||
/** The input event for mouse with a captured pointer */
|
||||
@RequiresApi(26)
|
||||
@Override
|
||||
public boolean dispatchCapturedPointerEvent(MotionEvent e) {
|
||||
CallbackBridge.mouseX += (e.getX()* mScaleFactor);
|
||||
CallbackBridge.mouseY += (e.getY()* mScaleFactor);
|
||||
if(!CallbackBridge.isGrabbing()){
|
||||
releasePointerCapture();
|
||||
clearFocus();
|
||||
}
|
||||
|
||||
if (mPointerDebugTextView.getVisibility() == View.VISIBLE && !debugErrored) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
try {
|
||||
builder.append("PointerCapture debug\n");
|
||||
builder.append("MotionEvent=").append(e.getActionMasked()).append("\n");
|
||||
builder.append("PressingBtn=").append(MotionEvent.class.getDeclaredMethod("buttonStateToString").invoke(null, e.getButtonState())).append("\n\n");
|
||||
|
||||
builder.append("PointerX=").append(e.getX()).append("\n");
|
||||
builder.append("PointerY=").append(e.getY()).append("\n");
|
||||
builder.append("RawX=").append(e.getRawX()).append("\n");
|
||||
builder.append("RawY=").append(e.getRawY()).append("\n\n");
|
||||
|
||||
builder.append("XPos=").append(CallbackBridge.mouseX).append("\n");
|
||||
builder.append("YPos=").append(CallbackBridge.mouseY).append("\n\n");
|
||||
builder.append("MovingX=").append(getMoving(e.getX(), true)).append("\n");
|
||||
builder.append("MovingY=").append(getMoving(e.getY(), false)).append("\n");
|
||||
} catch (Throwable th) {
|
||||
debugErrored = true;
|
||||
builder.append("Error getting debug. The debug will be stopped!\n").append(Log.getStackTraceString(th));
|
||||
} finally {
|
||||
mPointerDebugTextView.setText(builder.toString());
|
||||
builder.setLength(0);
|
||||
}
|
||||
}
|
||||
|
||||
mPointerDebugTextView.setText(CallbackBridge.DEBUG_STRING.toString());
|
||||
CallbackBridge.DEBUG_STRING.setLength(0);
|
||||
switch (e.getActionMasked()) {
|
||||
case MotionEvent.ACTION_MOVE:
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
return true;
|
||||
case MotionEvent.ACTION_BUTTON_PRESS:
|
||||
return sendMouseButtonUnconverted(e.getActionButton(), true);
|
||||
case MotionEvent.ACTION_BUTTON_RELEASE:
|
||||
return sendMouseButtonUnconverted(e.getActionButton(), false);
|
||||
case MotionEvent.ACTION_SCROLL:
|
||||
CallbackBridge.sendScroll(e.getAxisValue(MotionEvent.AXIS_HSCROLL), e.getAxisValue(MotionEvent.AXIS_VSCROLL));
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/** The event for keyboard/ gamepad button inputs */
|
||||
@Override
|
||||
public boolean onKeyPreIme(int keyCode, KeyEvent event) {
|
||||
//Toast.makeText(this, event.toString(),Toast.LENGTH_SHORT).show();
|
||||
//Toast.makeText(this, event.getDevice().toString(), Toast.LENGTH_SHORT).show();
|
||||
|
||||
//Filtering useless events by order of probability
|
||||
if((event.getFlags() & KeyEvent.FLAG_FALLBACK) == KeyEvent.FLAG_FALLBACK) return true;
|
||||
int eventKeycode = event.getKeyCode();
|
||||
if(eventKeycode == KeyEvent.KEYCODE_UNKNOWN) return true;
|
||||
if(eventKeycode == KeyEvent.KEYCODE_VOLUME_DOWN) return false;
|
||||
if(eventKeycode == KeyEvent.KEYCODE_VOLUME_UP) return false;
|
||||
if(event.getRepeatCount() != 0) return true;
|
||||
if(event.getAction() == KeyEvent.ACTION_MULTIPLE) return true;
|
||||
|
||||
//Toast.makeText(this, "FIRST VERIF PASSED", Toast.LENGTH_SHORT).show();
|
||||
|
||||
//Sometimes, key events comes from SOME keys of the software keyboard
|
||||
//Even weirder, is is unknown why a key or another is selected to trigger a keyEvent
|
||||
if((event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) == KeyEvent.FLAG_SOFT_KEYBOARD){
|
||||
if(eventKeycode == KeyEvent.KEYCODE_ENTER) return true; //We already listen to it.
|
||||
touchCharInput.dispatchKeyEvent(event);
|
||||
return true;
|
||||
}
|
||||
//Toast.makeText(this, "SECOND VERIF PASSED", Toast.LENGTH_SHORT).show();
|
||||
|
||||
|
||||
//Sometimes, key events may come from the mouse
|
||||
if(event.getDevice() != null
|
||||
&& ( (event.getSource() & InputDevice.SOURCE_MOUSE_RELATIVE) == InputDevice.SOURCE_MOUSE_RELATIVE
|
||||
|| (event.getSource() & InputDevice.SOURCE_MOUSE) == InputDevice.SOURCE_MOUSE) ){
|
||||
//Toast.makeText(this, "THE EVENT COMES FROM A MOUSE", Toast.LENGTH_SHORT).show();
|
||||
|
||||
|
||||
if(eventKeycode == KeyEvent.KEYCODE_BACK){
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, event.getAction() == KeyEvent.ACTION_DOWN);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
//System.out.println(event);
|
||||
|
||||
if(Gamepad.isGamepadEvent(event)){
|
||||
|
||||
if(mGamepad == null){
|
||||
mGamepad = new Gamepad(this, event.getDevice());
|
||||
}
|
||||
|
||||
mGamepad.update(event);
|
||||
return true;
|
||||
}
|
||||
|
||||
int index = EfficientAndroidLWJGLKeycode.getIndexByKey(eventKeycode);
|
||||
if(index >= 0) {
|
||||
//Toast.makeText(this,"THIS IS A KEYBOARD EVENT !", Toast.LENGTH_SHORT).show();
|
||||
EfficientAndroidLWJGLKeycode.execKey(event, index);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/** Get the mouse direction as a string */
|
||||
private String getMoving(float pos, boolean xOrY) {
|
||||
if (pos == 0) return "STOPPED";
|
||||
if (pos > 0) return xOrY ? "RIGHT" : "DOWN";
|
||||
return xOrY ? "LEFT" : "UP";
|
||||
}
|
||||
|
||||
/** Convert the mouse button, then send it
|
||||
* @return Whether the event was processed
|
||||
*/
|
||||
public static boolean sendMouseButtonUnconverted(int button, boolean status) {
|
||||
int glfwButton = -256;
|
||||
switch (button) {
|
||||
case MotionEvent.BUTTON_PRIMARY:
|
||||
glfwButton = LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT;
|
||||
break;
|
||||
case MotionEvent.BUTTON_TERTIARY:
|
||||
glfwButton = LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_MIDDLE;
|
||||
break;
|
||||
case MotionEvent.BUTTON_SECONDARY:
|
||||
glfwButton = LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT;
|
||||
break;
|
||||
}
|
||||
if(glfwButton == -256) return false;
|
||||
sendMouseButton(glfwButton, status);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/** @return the hotbar key, given the position. -1 if no key are pressed */
|
||||
public int handleGuiBar(int x, int y) {
|
||||
if (!CallbackBridge.isGrabbing()) return -1;
|
||||
|
||||
int barHeight = mcscale(20);
|
||||
int barY = CallbackBridge.physicalHeight - barHeight;
|
||||
if(y < barY) return -1;
|
||||
|
||||
int barWidth = mcscale(180);
|
||||
int barX = (CallbackBridge.physicalWidth / 2) - (barWidth / 2);
|
||||
if(x < barX || x >= barX + barWidth) return -1;
|
||||
|
||||
return HOTBAR_KEYS[(int) net.kdt.pojavlaunch.utils.MathUtils.map(x, barX, barX + barWidth, 0, 9)];
|
||||
}
|
||||
|
||||
/** Return the size, given the UI scale size */
|
||||
private int mcscale(int input) {
|
||||
return (int)((mGuiScale * input)/ mScaleFactor);
|
||||
}
|
||||
|
||||
/** Toggle the pointerDebugText visibility state */
|
||||
public void togglepointerDebugging() {
|
||||
mPointerDebugTextView.setVisibility(mPointerDebugTextView.getVisibility() == View.GONE ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
|
||||
/** Called when the size need to be set at any point during the surface lifecycle **/
|
||||
public void refreshSize(){
|
||||
windowWidth = Tools.getDisplayFriendlyRes(Tools.currentDisplayMetrics.widthPixels, mScaleFactor);
|
||||
windowHeight = Tools.getDisplayFriendlyRes(Tools.currentDisplayMetrics.heightPixels, mScaleFactor);
|
||||
if(LauncherPreferences.PREF_USE_ALTERNATE_SURFACE){
|
||||
((SurfaceView)mSurface).getHolder().setFixedSize(windowWidth, windowHeight);
|
||||
}else{
|
||||
((TextureView)mSurface).getSurfaceTexture().setDefaultBufferSize(windowWidth, windowHeight);
|
||||
}
|
||||
|
||||
CallbackBridge.sendUpdateWindowSize(windowWidth, windowHeight);
|
||||
//getMcScale();
|
||||
//Toast.makeText(getContext(), "width: " + width, Toast.LENGTH_SHORT).show();
|
||||
//Toast.makeText(getContext(), "height: " + height, Toast.LENGTH_SHORT).show();
|
||||
|
||||
}
|
||||
|
||||
private void realStart(Surface surface){
|
||||
// Initial size set
|
||||
refreshSize();
|
||||
|
||||
//Load Minecraft options:
|
||||
MCOptionUtils.load();
|
||||
MCOptionUtils.set("fullscreen", "off");
|
||||
MCOptionUtils.set("overrideWidth", String.valueOf(windowWidth));
|
||||
MCOptionUtils.set("overrideHeight", String.valueOf(windowHeight));
|
||||
MCOptionUtils.save();
|
||||
getMcScale();
|
||||
|
||||
JREUtils.setupBridgeWindow(surface);
|
||||
|
||||
new Thread(() -> {
|
||||
try {
|
||||
Thread.sleep(200);
|
||||
if(mSurfaceReadyListener != null){
|
||||
mSurfaceReadyListener.isReady();
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
Tools.showError(getContext(), e, true);
|
||||
}
|
||||
}, "JVM Main thread").start();
|
||||
}
|
||||
|
||||
/** A small interface called when the listener is ready for the first time */
|
||||
public interface SurfaceReadyListener {
|
||||
void isReady();
|
||||
}
|
||||
|
||||
public void setSurfaceReadyListener(SurfaceReadyListener listener){
|
||||
mSurfaceReadyListener = listener;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.content.*;
|
||||
import android.util.*;
|
||||
import android.view.*;
|
||||
|
||||
public class MinecraftGLView extends TextureView
|
||||
{
|
||||
public MinecraftGLView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public MinecraftGLView(Context context, AttributeSet attributeSet) {
|
||||
super(context, attributeSet);
|
||||
//Fixes freeform and dex mode having transparent glass,
|
||||
//since it forces android to used the background color of the view/layout behind it.
|
||||
setOpaque(false);
|
||||
setFocusable(true);
|
||||
}
|
||||
}
|
||||
|
|
@ -14,40 +14,37 @@ import java.util.*;
|
|||
|
||||
import net.kdt.pojavlaunch.utils.*;
|
||||
|
||||
public class PojavApplication extends Application
|
||||
{
|
||||
public class PojavApplication extends Application {
|
||||
public static String CRASH_REPORT_TAG = "PojavCrashReport";
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler(){
|
||||
@Override
|
||||
public void uncaughtException(Thread thread, Throwable th) {
|
||||
boolean storagePermAllowed = Build.VERSION.SDK_INT < 23 || ActivityCompat.checkSelfPermission(PojavApplication.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
File crashFile = new File(storagePermAllowed ? Tools.DIR_GAME_HOME : Tools.DIR_DATA, "latestcrash.txt");
|
||||
try {
|
||||
// Write to file, since some devices may not able to show error
|
||||
crashFile.getParentFile().mkdirs();
|
||||
crashFile.createNewFile();
|
||||
PrintStream crashStream = new PrintStream(crashFile);
|
||||
crashStream.append("PojavLauncher crash report\n");
|
||||
crashStream.append(" - Time: " + DateFormat.getDateTimeInstance().format(new Date()) + "\n");
|
||||
crashStream.append(" - Device: " + Build.PRODUCT + " " + Build.MODEL + "\n");
|
||||
crashStream.append(" - Android version: " + Build.VERSION.RELEASE + "\n");
|
||||
crashStream.append(" - Crash stack trace:\n");
|
||||
crashStream.append(" - Launcher version: " + BuildConfig.VERSION_NAME + "\n");
|
||||
crashStream.append(Log.getStackTraceString(th));
|
||||
crashStream.close();
|
||||
} catch (Throwable th2) {
|
||||
Log.e(CRASH_REPORT_TAG, " - Exception attempt saving crash stack trace:", th2);
|
||||
Log.e(CRASH_REPORT_TAG, " - The crash stack trace was:", th);
|
||||
}
|
||||
|
||||
FatalErrorActivity.showError(PojavApplication.this, crashFile.getAbsolutePath(), storagePermAllowed, th);
|
||||
// android.os.Process.killProcess(android.os.Process.myPid());
|
||||
|
||||
BaseMainActivity.fullyExit();
|
||||
Thread.setDefaultUncaughtExceptionHandler((thread, th) -> {
|
||||
boolean storagePermAllowed = Build.VERSION.SDK_INT < 23 || Build.VERSION.SDK_INT >= 29 ||
|
||||
ActivityCompat.checkSelfPermission(PojavApplication.this, android.Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED;
|
||||
File crashFile = new File(storagePermAllowed ? Tools.DIR_GAME_HOME : Tools.DIR_DATA, "latestcrash.txt");
|
||||
try {
|
||||
// Write to file, since some devices may not able to show error
|
||||
crashFile.getParentFile().mkdirs();
|
||||
crashFile.createNewFile();
|
||||
PrintStream crashStream = new PrintStream(crashFile);
|
||||
crashStream.append("PojavLauncher crash report\n");
|
||||
crashStream.append(" - Time: " + DateFormat.getDateTimeInstance().format(new Date()) + "\n");
|
||||
crashStream.append(" - Device: " + Build.PRODUCT + " " + Build.MODEL + "\n");
|
||||
crashStream.append(" - Android version: " + Build.VERSION.RELEASE + "\n");
|
||||
crashStream.append(" - Crash stack trace:\n");
|
||||
crashStream.append(" - Launcher version: " + BuildConfig.VERSION_NAME + "\n");
|
||||
crashStream.append(Log.getStackTraceString(th));
|
||||
crashStream.close();
|
||||
} catch (Throwable throwable) {
|
||||
Log.e(CRASH_REPORT_TAG, " - Exception attempt saving crash stack trace:", throwable);
|
||||
Log.e(CRASH_REPORT_TAG, " - The crash stack trace was:", th);
|
||||
}
|
||||
|
||||
FatalErrorActivity.showError(PojavApplication.this, crashFile.getAbsolutePath(), storagePermAllowed, th);
|
||||
// android.os.Process.killProcess(android.os.Process.myPid());
|
||||
|
||||
BaseMainActivity.fullyExit();
|
||||
});
|
||||
|
||||
try {
|
||||
|
|
@ -70,10 +67,9 @@ public class PojavApplication extends Application
|
|||
.concat("/x86");
|
||||
}
|
||||
|
||||
|
||||
} catch (Throwable th) {
|
||||
} catch (Throwable throwable) {
|
||||
Intent ferrorIntent = new Intent(this, FatalErrorActivity.class);
|
||||
ferrorIntent.putExtra("throwable", th);
|
||||
ferrorIntent.putExtra("throwable", throwable);
|
||||
startActivity(ferrorIntent);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static android.os.Build.VERSION_CODES.P;
|
||||
import static net.kdt.pojavlaunch.Tools.ignoreNotch;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_HIDE_SIDEBAR;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE;
|
||||
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Typeface;
|
||||
|
|
@ -9,126 +14,151 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.support.design.widget.VerticalTabLayout.ViewPagerAdapter;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Button;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.Guideline;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter;
|
||||
import androidx.viewpager2.widget.ViewPager2;
|
||||
|
||||
import net.kdt.pojavlaunch.extra.ExtraCore;
|
||||
import net.kdt.pojavlaunch.extra.ExtraListener;
|
||||
import net.kdt.pojavlaunch.fragments.ConsoleFragment;
|
||||
import net.kdt.pojavlaunch.fragments.CrashFragment;
|
||||
import net.kdt.pojavlaunch.fragments.LauncherFragment;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferenceFragment;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.prefs.screens.LauncherPreferenceFragment;
|
||||
import net.kdt.pojavlaunch.profiles.ProfileAdapter;
|
||||
import net.kdt.pojavlaunch.extra.ExtraConstants;
|
||||
import net.kdt.pojavlaunch.profiles.ProfileEditor;
|
||||
import net.kdt.pojavlaunch.profiles.ProfileIconCache;
|
||||
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static android.os.Build.VERSION_CODES.P;
|
||||
import static net.kdt.pojavlaunch.Tools.ignoreNotch;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_HIDE_SIDEBAR;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_IGNORE_NOTCH;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_NOTCH_SIZE;
|
||||
|
||||
public class PojavLauncherActivity extends BaseLauncherActivity
|
||||
{
|
||||
|
||||
private ViewPager viewPager;
|
||||
// An equivalent ViewPager2 adapter class
|
||||
private static class ScreenSlidePagerAdapter extends FragmentStateAdapter {
|
||||
public ScreenSlidePagerAdapter(FragmentActivity fa) {
|
||||
super(fa);
|
||||
}
|
||||
|
||||
private TextView tvUsernameView, tvConnectStatus;
|
||||
@Override
|
||||
public Fragment createFragment(int position) {
|
||||
if (position == 0) return new LauncherFragment();
|
||||
if (position == 1) return new ConsoleFragment();
|
||||
if (position == 2) return new CrashFragment();
|
||||
if (position == 3) return new LauncherPreferenceFragment();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private TextView tvConnectStatus;
|
||||
private Spinner accountSelector;
|
||||
private ViewPagerAdapter viewPageAdapter;
|
||||
private ViewPager2 viewPager;
|
||||
private final Button[] Tabs = new Button[4];
|
||||
private View selected;
|
||||
private View selectedTab;
|
||||
private ImageView accountFaceImageView;
|
||||
|
||||
private Button logoutBtn; // MineButtons
|
||||
private ExtraListener backPreferenceListener;
|
||||
|
||||
public PojavLauncherActivity() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
ExtraCore.removeExtraListenerFromValue(ExtraConstants.BACK_PREFERENCE, backPreferenceListener);
|
||||
super.onDestroy();
|
||||
ProfileIconCache.clearIconCache();
|
||||
Log.i("LauncherActivity","Destroyed!");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.launcher_main_v4);
|
||||
|
||||
|
||||
if (BuildConfig.DEBUG) {
|
||||
Toast.makeText(this, "Launcher process id: " + android.os.Process.myPid(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_pojav_launcher);
|
||||
|
||||
//Boilerplate linking/initialisation
|
||||
viewPager = findViewById(R.id.launchermainTabPager);
|
||||
selected = findViewById(R.id.viewTabSelected);
|
||||
|
||||
mConsoleView = new ConsoleFragment();
|
||||
mCrashView = new CrashFragment();
|
||||
|
||||
viewPageAdapter = new ViewPagerAdapter(getSupportFragmentManager());
|
||||
viewPageAdapter.addFragment(new LauncherFragment(), 0, getString(R.string.mcl_tab_news));
|
||||
viewPageAdapter.addFragment(mConsoleView, 0, getString(R.string.mcl_tab_console));
|
||||
viewPageAdapter.addFragment(mCrashView, 0, getString(R.string.mcl_tab_crash));
|
||||
viewPageAdapter.addFragment(new LauncherPreferenceFragment(), 0, getString(R.string.mcl_option_settings));
|
||||
|
||||
viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
setTabActive(position);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPageScrollStateChanged(int state) {
|
||||
|
||||
}
|
||||
});
|
||||
viewPager.setAdapter(viewPageAdapter);
|
||||
|
||||
tvConnectStatus = (TextView) findViewById(R.id.launchermain_text_accountstatus);
|
||||
tvUsernameView = (TextView) findViewById(R.id.launchermain_text_welcome);
|
||||
mTextVersion = (TextView) findViewById(R.id.launcherMainVersionView);
|
||||
|
||||
//The following line is used to make this TextView horizontally scroll if the version name is larger than the view
|
||||
mTextVersion.setSelected(true);
|
||||
|
||||
selectedTab = findViewById(R.id.viewTabSelected);
|
||||
tvConnectStatus = findViewById(R.id.launchermain_text_accountstatus);
|
||||
accountFaceImageView = findViewById(R.id.launchermain_account_image);
|
||||
accountSelector = findViewById(R.id.launchermain_spinner_account);
|
||||
mVersionSelector = findViewById(R.id.launchermain_spinner_version);
|
||||
mLaunchProgress = findViewById(R.id.progressDownloadBar);
|
||||
mLaunchTextStatus = findViewById(R.id.progressDownloadText);
|
||||
logoutBtn = findViewById(R.id.installJarButton);
|
||||
mPlayButton = findViewById(R.id.launchermainPlayButton);
|
||||
Tabs[0] = findViewById(R.id.btnTab1);
|
||||
Tabs[1] = findViewById(R.id.btnTab2);
|
||||
Tabs[2] = findViewById(R.id.btnTab3);
|
||||
Tabs[3] = findViewById(R.id.btnTab4);
|
||||
|
||||
|
||||
pickAccount();
|
||||
if (BuildConfig.DEBUG) {
|
||||
Toast.makeText(this, "Launcher process id: " + android.os.Process.myPid(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
||||
// Setup the viewPager to slide across fragments
|
||||
viewPager.setAdapter(new ScreenSlidePagerAdapter(this));
|
||||
viewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
|
||||
@Override
|
||||
public void onPageSelected(int position) {
|
||||
setTabActive(position);
|
||||
}
|
||||
});
|
||||
initTabs(0);
|
||||
|
||||
//Setup listener to the backPreference system
|
||||
backPreferenceListener = (key, value) -> {
|
||||
if(value.equals("true")){
|
||||
onBackPressed();
|
||||
ExtraCore.setValue(key, "false");
|
||||
}
|
||||
return false;
|
||||
};
|
||||
ExtraCore.addExtraListener(ExtraConstants.BACK_PREFERENCE, backPreferenceListener);
|
||||
|
||||
|
||||
final List<String> accountList = new ArrayList<String>();
|
||||
final MinecraftAccount tempProfile = PojavProfile.getTempProfileContent(this);
|
||||
// Try to load the temporary account
|
||||
final List<String> accountList = new ArrayList<>();
|
||||
final MinecraftAccount tempProfile = PojavProfile.getTempProfileContent();
|
||||
if (tempProfile != null) {
|
||||
accountList.add(tempProfile.username);
|
||||
}
|
||||
for (String s : new File(Tools.DIR_ACCOUNT_NEW).list()) {
|
||||
accountList.add(s.substring(0, s.length() - 5));
|
||||
}
|
||||
|
||||
|
||||
// Setup account spinner
|
||||
pickAccount();
|
||||
ArrayAdapter<String> adapterAcc = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, accountList);
|
||||
adapterAcc.setDropDownViewResource(android.R.layout.simple_list_item_single_choice);
|
||||
accountSelector = (Spinner) findViewById(R.id.launchermain_spinner_account);
|
||||
accountSelector.setAdapter(adapterAcc);
|
||||
|
||||
if (tempProfile != null) {
|
||||
accountSelector.setSelection(0);
|
||||
} else {
|
||||
|
|
@ -157,13 +187,77 @@ public class PojavLauncherActivity extends BaseLauncherActivity
|
|||
// TODO: Implement this method
|
||||
}
|
||||
});
|
||||
|
||||
List<String> versions = new ArrayList<String>();
|
||||
|
||||
// Setup the minecraft version list
|
||||
setupBasicList(this);
|
||||
|
||||
//mAvailableVersions;
|
||||
ProfileAdapter profileAdapter = new ProfileAdapter(this, true);
|
||||
ProfileEditor profileEditor = new ProfileEditor(this,(name, isNew, deleting)->{
|
||||
LauncherProfiles.update();
|
||||
if(isNew) {
|
||||
mVersionSelector.setSelection(profileAdapter.resolveProfileIndex(name));
|
||||
}
|
||||
if(deleting) {
|
||||
mVersionSelector.setSelection(0);
|
||||
}
|
||||
profileAdapter.notifyDataSetChanged();
|
||||
});
|
||||
mVersionSelector.setOnLongClickListener((v)->profileEditor.show(LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE,"")));
|
||||
mVersionSelector.setAdapter(profileAdapter);
|
||||
mVersionSelector.setSelection(profileAdapter.resolveProfileIndex(LauncherPreferences.DEFAULT_PREF.getString(LauncherPreferences.PREF_KEY_CURRENT_PROFILE,"")));
|
||||
mVersionSelector.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener(){
|
||||
@Override
|
||||
public void onItemSelected(AdapterView<?> p1, View p2, int p3, long p4)
|
||||
{
|
||||
String profileName = p1.getItemAtPosition(p3).toString();
|
||||
if(profileName.equals(ProfileAdapter.CREATE_PROFILE_MAGIC)) {
|
||||
profileEditor.show(profileName);
|
||||
mVersionSelector.setSelection(0);
|
||||
return;
|
||||
}
|
||||
LauncherPreferences.DEFAULT_PREF.edit()
|
||||
.putString(
|
||||
LauncherPreferences.PREF_KEY_CURRENT_PROFILE,
|
||||
p1.getItemAtPosition(p3).toString())
|
||||
.commit();
|
||||
}
|
||||
@Override
|
||||
public void onNothingSelected(AdapterView<?> p1)
|
||||
{
|
||||
// TODO: Implement this method
|
||||
}
|
||||
});
|
||||
//
|
||||
statusIsLaunching(false);
|
||||
|
||||
|
||||
//Add the preference changed listener
|
||||
LauncherPreferences.DEFAULT_PREF.registerOnSharedPreferenceChangeListener((sharedPreferences, key) -> {
|
||||
if(key.equals("hideSidebar")){
|
||||
changeLookAndFeel(sharedPreferences.getBoolean("hideSidebar",false));
|
||||
return;
|
||||
}
|
||||
|
||||
if(key.equals("ignoreNotch")){
|
||||
ignoreNotch(sharedPreferences.getBoolean("ignoreNotch", true), PojavLauncherActivity.this);
|
||||
return;
|
||||
}
|
||||
});
|
||||
changeLookAndFeel(PREF_HIDE_SIDEBAR);
|
||||
}
|
||||
private void selectTabPage(int pageIndex){
|
||||
viewPager.setCurrentItem(pageIndex);
|
||||
setTabActive(pageIndex);
|
||||
}
|
||||
public static String[] basicVersionList;
|
||||
public static void setupBasicList(Context ctx) {
|
||||
List<String> versions = new ArrayList<>();
|
||||
final File fVers = new File(Tools.DIR_HOME_VERSION);
|
||||
|
||||
try {
|
||||
if (fVers.listFiles().length < 1) {
|
||||
throw new Exception(getString(R.string.error_no_version));
|
||||
throw new Exception(ctx.getString(R.string.error_no_version));
|
||||
}
|
||||
|
||||
for (File fVer : fVers.listFiles()) {
|
||||
|
|
@ -171,59 +265,21 @@ public class PojavLauncherActivity extends BaseLauncherActivity
|
|||
versions.add(fVer.getName());
|
||||
}
|
||||
} catch (Exception e) {
|
||||
versions.add(getString(R.string.global_error) + ":");
|
||||
versions.add(ctx.getString(R.string.global_error) + ":");
|
||||
versions.add(e.getMessage());
|
||||
|
||||
} finally {
|
||||
mAvailableVersions = versions.toArray(new String[0]);
|
||||
basicVersionList = versions.toArray(new String[0]);
|
||||
ExtraCore.setValue(ExtraConstants.VERSION_LIST,versions);
|
||||
}
|
||||
|
||||
//mAvailableVersions;
|
||||
ArrayAdapter<String> adapterVer = new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, mAvailableVersions);
|
||||
adapterVer.setDropDownViewResource(android.R.layout.simple_list_item_single_choice);
|
||||
mVersionSelector = (Spinner) findViewById(R.id.launchermain_spinner_version);
|
||||
mVersionSelector.setAdapter(adapterVer);
|
||||
|
||||
mLaunchProgress = (ProgressBar) findViewById(R.id.progressDownloadBar);
|
||||
mLaunchTextStatus = (TextView) findViewById(R.id.progressDownloadText);
|
||||
logoutBtn = (Button) findViewById(R.id.switchUserBtn);
|
||||
|
||||
mPlayButton = (Button) findViewById(R.id.launchermainPlayButton);
|
||||
|
||||
statusIsLaunching(false);
|
||||
|
||||
|
||||
initTabs(0);
|
||||
LauncherPreferences.DEFAULT_PREF.registerOnSharedPreferenceChangeListener(new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
if(key.equals("hideSidebar")){
|
||||
changeLookAndFeel(sharedPreferences.getBoolean("hideSidebar",false));
|
||||
return;
|
||||
}
|
||||
|
||||
if(key.equals("ignoreNotch")){
|
||||
ignoreNotch(sharedPreferences.getBoolean("ignoreNotch", true), PojavLauncherActivity.this);
|
||||
return;
|
||||
}
|
||||
}
|
||||
});
|
||||
changeLookAndFeel(PREF_HIDE_SIDEBAR);
|
||||
ignoreNotch(PREF_IGNORE_NOTCH, PojavLauncherActivity.this);
|
||||
}
|
||||
|
||||
|
||||
private void selectTabPage(int pageIndex){
|
||||
viewPager.setCurrentItem(pageIndex);
|
||||
setTabActive(pageIndex);
|
||||
}
|
||||
|
||||
private void pickAccount() {
|
||||
try {
|
||||
mProfile = PojavProfile.getCurrentProfileContent(this);
|
||||
accountFaceImageView.setImageBitmap(mProfile.getSkinFace());
|
||||
|
||||
tvUsernameView.setText(getString(R.string.main_welcome, mProfile.username));
|
||||
tvConnectStatus.setText(mProfile.accessToken.equals("0") ? R.string.mcl_account_offline : R.string.mcl_account_connected);
|
||||
//TODO FULL BACKGROUND LOGIN
|
||||
tvConnectStatus.setText(mProfile.accessToken.equals("0") ? R.string.mcl_account_local : R.string.mcl_account_connected);
|
||||
} catch(Exception e) {
|
||||
mProfile = new MinecraftAccount();
|
||||
Tools.showError(this, e, true);
|
||||
|
|
@ -259,21 +315,18 @@ public class PojavLauncherActivity extends BaseLauncherActivity
|
|||
Tabs[index].setTextColor(Color.WHITE);
|
||||
|
||||
//Animating the white bar on the left
|
||||
ValueAnimator animation = ValueAnimator.ofFloat(selected.getY(), Tabs[index].getY()+(Tabs[index].getHeight()-selected.getHeight())/2f);
|
||||
ValueAnimator animation = ValueAnimator.ofFloat(selectedTab.getY(), Tabs[index].getY()+(Tabs[index].getHeight()- selectedTab.getHeight())/2f);
|
||||
animation.setDuration(250);
|
||||
animation.addUpdateListener(animation1 -> selected.setY((float) animation1.getAnimatedValue()));
|
||||
animation.addUpdateListener(animation1 -> selectedTab.setY((float) animation1.getAnimatedValue()));
|
||||
animation.start();
|
||||
}
|
||||
|
||||
protected void initTabs(int activeTab){
|
||||
final Handler handler = new Handler(Looper.getMainLooper());
|
||||
handler.postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
//Do something after 100ms
|
||||
selectTabPage(activeTab);
|
||||
}
|
||||
}, 500);
|
||||
handler.post(() -> {
|
||||
//Do something after 100ms
|
||||
selectTabPage(activeTab);
|
||||
});
|
||||
}
|
||||
|
||||
private void changeLookAndFeel(boolean useOldLook){
|
||||
|
|
@ -286,8 +339,9 @@ public class PojavLauncherActivity extends BaseLauncherActivity
|
|||
params.guidePercent = 0; // 0%, range: 0 <-> 1
|
||||
guideLine.setLayoutParams(params);
|
||||
|
||||
//Remove the selected Tab
|
||||
selected.setVisibility(View.GONE);
|
||||
//Remove the selected Tab and the head image
|
||||
selectedTab.setVisibility(View.GONE);
|
||||
accountFaceImageView.setVisibility(View.GONE);
|
||||
|
||||
//Enlarge the button, but just a bit.
|
||||
params = (ConstraintLayout.LayoutParams) mPlayButton.getLayoutParams();
|
||||
|
|
@ -299,7 +353,8 @@ public class PojavLauncherActivity extends BaseLauncherActivity
|
|||
guideLine.setLayoutParams(params);
|
||||
|
||||
//Show the selected Tab
|
||||
selected.setVisibility(View.VISIBLE);
|
||||
selectedTab.setVisibility(View.VISIBLE);
|
||||
accountFaceImageView.setVisibility(View.VISIBLE);
|
||||
|
||||
//Set the default button size
|
||||
params = (ConstraintLayout.LayoutParams) mPlayButton.getLayoutParams();
|
||||
|
|
@ -323,5 +378,22 @@ public class PojavLauncherActivity extends BaseLauncherActivity
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Custom back stack system. Use the classic backstack when the focus is on the setting screen,
|
||||
* finish the activity and remove the back_preference listener otherwise
|
||||
*/
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
int count = getSupportFragmentManager().getBackStackEntryCount();
|
||||
|
||||
if(count > 0 && viewPager.getCurrentItem() == 3){
|
||||
getSupportFragmentManager().popBackStack();
|
||||
}else{
|
||||
super.onBackPressed();
|
||||
//additional code
|
||||
ExtraCore.removeExtraListenerFromValue(ExtraConstants.BACK_PREFERENCE, backPreferenceListener);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static net.kdt.pojavlaunch.Architecture.archAsString;
|
||||
import static net.kdt.pojavlaunch.Tools.getFileName;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.Activity;
|
||||
|
|
@ -9,10 +10,7 @@ import android.content.Intent;
|
|||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.AssetManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
|
|
@ -21,7 +19,6 @@ import android.text.Html;
|
|||
import android.text.SpannableString;
|
||||
import android.text.Spanned;
|
||||
import android.text.style.StyleSpan;
|
||||
import android.util.Base64;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
|
@ -50,33 +47,41 @@ import net.kdt.pojavlaunch.authenticator.mojang.InvalidateTokenTask;
|
|||
import net.kdt.pojavlaunch.authenticator.mojang.LoginListener;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.LoginTask;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener;
|
||||
import net.kdt.pojavlaunch.colorselector.ColorSelector;
|
||||
import net.kdt.pojavlaunch.customcontrols.CustomControls;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTConfigDialog;
|
||||
import net.kdt.pojavlaunch.multirt.MultiRTUtils;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.utils.LocaleUtils;
|
||||
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
||||
import net.kdt.pojavlaunch.value.PerVersionConfig;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
||||
import org.apache.commons.compress.utils.IOUtils;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Locale;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
||||
public class PojavLoginActivity extends BaseActivity
|
||||
// MineActivity
|
||||
{
|
||||
public class PojavLoginActivity extends BaseActivity {
|
||||
private final Object mLockStoragePerm = new Object();
|
||||
private final Object mLockSelectJRE = new Object();
|
||||
|
||||
private EditText edit2, edit3;
|
||||
private final int REQUEST_STORAGE_REQUEST_CODE = 1;
|
||||
private CheckBox sRemember, sOffline;
|
||||
private CheckBox sRemember, sLocal;
|
||||
private TextView startupTextView;
|
||||
private SharedPreferences firstLaunchPrefs;
|
||||
private MinecraftAccount mProfile = null;
|
||||
|
|
@ -172,7 +177,7 @@ public class PojavLoginActivity extends BaseActivity
|
|||
}
|
||||
}
|
||||
private void uiInit() {
|
||||
setContentView(R.layout.launcher_login_v3);
|
||||
setContentView(R.layout.activity_pojav_login);
|
||||
|
||||
Spinner spinnerChgLang = findViewById(R.id.login_spinner_language);
|
||||
|
||||
|
|
@ -241,16 +246,14 @@ public class PojavLoginActivity extends BaseActivity
|
|||
public void onNothingSelected(AdapterView<?> adapter) {}
|
||||
});
|
||||
|
||||
edit2 = (EditText) findViewById(R.id.login_edit_email);
|
||||
edit3 = (EditText) findViewById(R.id.login_edit_password);
|
||||
|
||||
edit2 = findViewById(R.id.login_edit_email);
|
||||
edit3 = findViewById(R.id.login_edit_password);
|
||||
sRemember = findViewById(R.id.login_switch_remember);
|
||||
sOffline = findViewById(R.id.login_switch_offline);
|
||||
sOffline.setOnCheckedChangeListener((p1, p2) -> {
|
||||
sLocal = findViewById(R.id.login_switch_local);
|
||||
sLocal.setOnCheckedChangeListener((p1, p2) -> {
|
||||
// May delete later
|
||||
edit3.setEnabled(!p2);
|
||||
});
|
||||
|
||||
isSkipInit = true;
|
||||
}
|
||||
|
||||
|
|
@ -264,8 +267,41 @@ public class PojavLoginActivity extends BaseActivity
|
|||
PojavProfile.setCurrentProfile(this, null);
|
||||
}
|
||||
|
||||
|
||||
private void unpackComponent(AssetManager am, String component) throws IOException {
|
||||
private void repackLWJGL3(boolean ignoreExistence) throws IOException {
|
||||
File outFile = new File(Tools.DIR_GAME_HOME + "/lwjgl3/lwjgl-fat.jar");
|
||||
File outTmpFile = new File(Tools.DIR_GAME_HOME + "/lwjgl3/lwjgl-fat.jar.tmp");
|
||||
File[] innerFiles = outTmpFile.getParentFile().listFiles();
|
||||
|
||||
if (outFile.exists() && !ignoreExistence) {
|
||||
return;
|
||||
}
|
||||
outFile.delete();
|
||||
outTmpFile.delete();
|
||||
|
||||
java.util.jar.Manifest manifest = new java.util.jar.Manifest();
|
||||
manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
|
||||
JarOutputStream outJar = new JarOutputStream(new FileOutputStream(outTmpFile), manifest);
|
||||
outJar.setLevel(9);
|
||||
|
||||
for (File file : innerFiles) {
|
||||
if (!file.getName().endsWith(".jar")) continue;
|
||||
JarInputStream inJar = new JarInputStream(new FileInputStream(file));
|
||||
JarEntry entry;
|
||||
while ((entry = inJar.getNextJarEntry()) != null) {
|
||||
// avoid some duplicated entries
|
||||
if (entry.getSize() == 0 || entry.getName().equals("META-INF/INDEX.LIST") || entry.getName().startsWith("META-INF/versions")) continue;
|
||||
outJar.putNextEntry(new JarEntry(entry.getName()));
|
||||
IOUtils.copy(inJar, outJar);
|
||||
}
|
||||
inJar.close();
|
||||
file.delete();
|
||||
}
|
||||
|
||||
outJar.close();
|
||||
outTmpFile.renameTo(outFile);
|
||||
}
|
||||
|
||||
private boolean unpackComponent(AssetManager am, String component) throws IOException {
|
||||
File versionFile = new File(Tools.DIR_GAME_HOME + "/" + component + "/version");
|
||||
InputStream is = am.open("components/" + component + "/version");
|
||||
if(!versionFile.exists()) {
|
||||
|
|
@ -295,8 +331,10 @@ public class PojavLoginActivity extends BaseActivity
|
|||
}
|
||||
} else {
|
||||
Log.i("UnpackPrep", component + ": Pack is up-to-date with the launcher, continuing...");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
public static void disableSplash(String dir) {
|
||||
mkdirs(dir + "/config");
|
||||
|
|
@ -339,16 +377,22 @@ public class PojavLoginActivity extends BaseActivity
|
|||
// TODO: Remove after implement.
|
||||
Tools.copyAssetFile(this, "launcher_profiles.json", Tools.DIR_GAME_NEW, false);
|
||||
Tools.copyAssetFile(this,"resolv.conf",Tools.DIR_DATA, true);
|
||||
Tools.copyAssetFile(this,"arc_dns_injector.jar",Tools.DIR_DATA, true);
|
||||
AssetManager am = this.getAssets();
|
||||
|
||||
unpackComponent(am, "caciocavallo");
|
||||
unpackComponent(am, "lwjgl3");
|
||||
|
||||
// Since the Java module system doesn't allow multiple JARs to declare the same module,
|
||||
// we repack them to a single file here
|
||||
repackLWJGL3(unpackComponent(am, "lwjgl3"));
|
||||
|
||||
if(!installRuntimeAutomatically(am,MultiRTUtils.getRuntimes().size() > 0)) {
|
||||
MultiRTConfigDialog.openRuntimeSelector(this, MultiRTConfigDialog.MULTIRT_PICK_RUNTIME_STARTUP);
|
||||
synchronized (mLockSelectJRE) {
|
||||
mLockSelectJRE.wait();
|
||||
}
|
||||
}
|
||||
migrateToProfiles();
|
||||
if(Build.VERSION.SDK_INT > 28) runOnUiThread(this::showStorageDialog);
|
||||
LauncherPreferences.loadPreferences(getApplicationContext());
|
||||
}
|
||||
|
|
@ -379,7 +423,7 @@ public class PojavLoginActivity extends BaseActivity
|
|||
final Uri uri = data.getData();
|
||||
Thread t = new Thread(() -> {
|
||||
try {
|
||||
MultiRTUtils.installRuntimeNamed(getContentResolver().openInputStream(uri), BaseLauncherActivity.getFileName(this, uri),
|
||||
MultiRTUtils.installRuntimeNamed(getContentResolver().openInputStream(uri), getFileName(this, uri),
|
||||
(resid, stuff) -> PojavLoginActivity.this.runOnUiThread(
|
||||
() -> {
|
||||
if (startupTextView != null)
|
||||
|
|
@ -401,6 +445,46 @@ public class PojavLoginActivity extends BaseActivity
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateToProfiles() {
|
||||
try {
|
||||
if(!PerVersionConfig.exists()) return;
|
||||
LauncherProfiles.update();
|
||||
PerVersionConfig.update();
|
||||
if(PerVersionConfig.erase()) {
|
||||
for (String version : PerVersionConfig.configMap.keySet()) {
|
||||
PerVersionConfig.VersionConfig config = PerVersionConfig.configMap.get(version);
|
||||
if(config == null) continue; // Skip the version
|
||||
|
||||
// Replaced by gl4es_extra
|
||||
if(config.renderer != null) {
|
||||
if (config.renderer.contains("zink")) config.renderer = "opengles3_virgl";
|
||||
if (!config.renderer.contains("virgl")) config.renderer = null;
|
||||
}
|
||||
|
||||
if(config.renderer == null && config.gamePath == null &&
|
||||
config.jvmArgs == null && config.selectedRuntime == null){
|
||||
continue; // Empty pvc, skip it.
|
||||
}
|
||||
|
||||
MinecraftProfile profile = new MinecraftProfile();
|
||||
profile.lastVersionId = version;
|
||||
profile.name = getString(R.string.migrated_profile_str, version);
|
||||
profile.pojavRendererName = config.renderer;
|
||||
profile.gameDir = config.gamePath;
|
||||
profile.javaDir = Tools.LAUNCHERPROFILES_RTPREFIX + config.selectedRuntime;
|
||||
profile.javaArgs = config.jvmArgs;
|
||||
LauncherProfiles.mainProfileJson.profiles.put("pvc-migrated-" + version, profile);
|
||||
}
|
||||
LauncherProfiles.update();
|
||||
}else{
|
||||
Log.e("ProfileMigrator"," Unable to remove Per Version Config files.");
|
||||
}
|
||||
}catch (IOException e) {
|
||||
Log.e("ProfileMigrator","Failed to migrate!",e);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean installRuntimeAutomatically(AssetManager am, boolean otherRuntimesAvailable) {
|
||||
/* Check if JRE is included */
|
||||
String rt_version = null;
|
||||
|
|
@ -410,11 +494,11 @@ public class PojavLoginActivity extends BaseActivity
|
|||
} catch (IOException e) {
|
||||
Log.e("JREAuto", "JRE was not included on this APK.", e);
|
||||
}
|
||||
if(current_rt_version == null && otherRuntimesAvailable) return true; //Assume user maintains his own runtime
|
||||
if(rt_version == null) return false;
|
||||
if(current_rt_version == null && MultiRTUtils.getExactJreName(8) != null) return true; //Assume user maintains his own runtime
|
||||
if(rt_version == null) return otherRuntimesAvailable; // On noruntime builds, skip if there is at least 1 runtime installed (no matter if it is 8 or not)
|
||||
if(!rt_version.equals(current_rt_version)) { //If we already have an integrated one installed, check if it's up-to-date
|
||||
try {
|
||||
MultiRTUtils.installRuntimeNamedBinpack(am.open("components/jre/universal.tar.xz"), am.open("components/jre/bin-" + archAsString(Tools.DEVICE_ARCHITECTURE) + ".tar.xz"), "Internal", rt_version,
|
||||
MultiRTUtils.installRuntimeNamedBinpack(getApplicationInfo().nativeLibraryDir, am.open("components/jre/universal.tar.xz"), am.open("components/jre/bin-" + archAsString(Tools.DEVICE_ARCHITECTURE) + ".tar.xz"), "Internal", rt_version,
|
||||
(resid, vararg) -> runOnUiThread(()->{if(startupTextView!=null)startupTextView.setText(getString(resid,vararg));}));
|
||||
MultiRTUtils.postPrepare(PojavLoginActivity.this,"Internal");
|
||||
return true;
|
||||
|
|
@ -494,27 +578,21 @@ public class PojavLoginActivity extends BaseActivity
|
|||
|
||||
final Dialog accountDialog = new Dialog(PojavLoginActivity.this);
|
||||
|
||||
accountDialog.setContentView(R.layout.simple_account_list_holder);
|
||||
accountDialog.setContentView(R.layout.dialog_select_account);
|
||||
|
||||
LinearLayout accountListLayout = accountDialog.findViewById(R.id.accountListLayout);
|
||||
LayoutInflater inflater = (LayoutInflater) this.getSystemService(LAYOUT_INFLATER_SERVICE);
|
||||
|
||||
for (int accountIndex = 0; accountIndex < accountArr.length; accountIndex++) {
|
||||
String s = accountArr[accountIndex];
|
||||
View child = inflater.inflate(R.layout.simple_account_list_item, accountListLayout,false);
|
||||
View child = inflater.inflate(R.layout.item_minecraft_account, accountListLayout,false);
|
||||
TextView accountName = child.findViewById(R.id.accountitem_text_name);
|
||||
ImageButton removeButton = child.findViewById(R.id.accountitem_button_remove);
|
||||
ImageView imageView = child.findViewById(R.id.account_head);
|
||||
|
||||
String accNameStr = s.substring(0, s.length() - 5);
|
||||
String skinFaceBase64 = MinecraftAccount.load(accNameStr).skinFaceBase64;
|
||||
if (skinFaceBase64 != null) {
|
||||
byte[] faceIconBytes = Base64.decode(skinFaceBase64, Base64.DEFAULT);
|
||||
Bitmap bitmap = BitmapFactory.decodeByteArray(faceIconBytes, 0, faceIconBytes.length);
|
||||
imageView.setImageBitmap(MinecraftAccount.load(accNameStr).getSkinFace());
|
||||
|
||||
imageView.setImageDrawable(new BitmapDrawable(getResources(),
|
||||
bitmap));
|
||||
}
|
||||
accountName.setText(accNameStr);
|
||||
|
||||
accountListLayout.addView(child);
|
||||
|
|
@ -542,8 +620,6 @@ public class PojavLoginActivity extends BaseActivity
|
|||
if (acc.isMicrosoft){
|
||||
new MicrosoftAuthTask(PojavLoginActivity.this, authListener)
|
||||
.execute("true", acc.msaRefreshToken);
|
||||
} else if (acc.accessToken.length() >= 5) {
|
||||
PojavProfile.updateTokens(PojavLoginActivity.this, selectedAccName, authListener);
|
||||
} else {
|
||||
accountDialog.dismiss();
|
||||
PojavProfile.launch(PojavLoginActivity.this, selectedAccName);
|
||||
|
|
@ -584,7 +660,7 @@ public class PojavLoginActivity extends BaseActivity
|
|||
accountDialog.show();
|
||||
}
|
||||
|
||||
private MinecraftAccount loginOffline() {
|
||||
private MinecraftAccount loginLocal() {
|
||||
new File(Tools.DIR_ACCOUNT_OLD).mkdir();
|
||||
|
||||
String text = edit2.getText().toString();
|
||||
|
|
@ -594,8 +670,6 @@ public class PojavLoginActivity extends BaseActivity
|
|||
edit2.setError(getString(R.string.login_error_invalid_username));
|
||||
} else if (new File(Tools.DIR_ACCOUNT_NEW + "/" + text + ".json").exists()) {
|
||||
edit2.setError(getString(R.string.login_error_exist_username));
|
||||
} else if (!edit3.getText().toString().isEmpty()) {
|
||||
edit3.setError(getString(R.string.login_error_offline_password));
|
||||
} else {
|
||||
MinecraftAccount builder = new MinecraftAccount();
|
||||
builder.isMicrosoft = false;
|
||||
|
|
@ -605,47 +679,45 @@ public class PojavLoginActivity extends BaseActivity
|
|||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void loginMC(final View v)
|
||||
{
|
||||
|
||||
if (sOffline.isChecked()) {
|
||||
mProfile = loginOffline();
|
||||
if (sLocal.isChecked()) {
|
||||
mProfile = loginLocal();
|
||||
playProfile(false);
|
||||
} else {
|
||||
ProgressBar prb = findViewById(R.id.launcherAccProgress);
|
||||
new LoginTask().setLoginListener(new LoginListener(){
|
||||
|
||||
|
||||
@Override
|
||||
public void onBeforeLogin() {
|
||||
v.setEnabled(false);
|
||||
prb.setVisibility(View.VISIBLE);
|
||||
}
|
||||
@Override
|
||||
public void onBeforeLogin() {
|
||||
v.setEnabled(false);
|
||||
prb.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoginDone(String[] result) {
|
||||
if(result[0].equals("ERROR")){
|
||||
Tools.dialogOnUiThread(PojavLoginActivity.this,
|
||||
@Override
|
||||
public void onLoginDone(String[] result) {
|
||||
if(result[0].equals("ERROR")){
|
||||
Tools.dialogOnUiThread(PojavLoginActivity.this,
|
||||
getResources().getString(R.string.global_error), strArrToString(result));
|
||||
} else{
|
||||
MinecraftAccount builder = new MinecraftAccount();
|
||||
builder.accessToken = result[1];
|
||||
builder.clientToken = result[2];
|
||||
builder.profileId = result[3];
|
||||
builder.username = result[4];
|
||||
builder.selectedVersion = "1.12.2";
|
||||
builder.updateSkinFace();
|
||||
mProfile = builder;
|
||||
}
|
||||
runOnUiThread(() -> {
|
||||
v.setEnabled(true);
|
||||
prb.setVisibility(View.GONE);
|
||||
playProfile(false);
|
||||
});
|
||||
} else{
|
||||
MinecraftAccount builder = new MinecraftAccount();
|
||||
builder.accessToken = result[1];
|
||||
builder.clientToken = result[2];
|
||||
builder.profileId = result[3];
|
||||
builder.username = result[4];
|
||||
builder.updateSkinFace();
|
||||
mProfile = builder;
|
||||
}
|
||||
}).execute(edit2.getText().toString(), edit3.getText().toString());
|
||||
runOnUiThread(() -> {
|
||||
v.setEnabled(true);
|
||||
prb.setVisibility(View.GONE);
|
||||
playProfile(false);
|
||||
});
|
||||
}
|
||||
}).execute(edit2.getText().toString(), edit3.getText().toString());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,7 @@ import net.kdt.pojavlaunch.value.*;
|
|||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.IOUtils;
|
||||
|
||||
public class PojavMigrator
|
||||
{
|
||||
public class PojavMigrator {
|
||||
public static void migrateAccountData(Context ctx) {
|
||||
File oldAccDir = new File(Tools.DIR_ACCOUNT_OLD);
|
||||
if (oldAccDir.exists() && oldAccDir.isDirectory()) {
|
||||
|
|
|
|||
|
|
@ -8,14 +8,14 @@ import android.util.Log;
|
|||
import com.google.gson.JsonSyntaxException;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.RefreshListener;
|
||||
import net.kdt.pojavlaunch.authenticator.mojang.RefreshTokenTask;
|
||||
import net.kdt.pojavlaunch.value.MinecraftAccount;
|
||||
|
||||
public class PojavProfile
|
||||
{
|
||||
private static String PROFILE_PREF = "pojav_profile";
|
||||
private static String PROFILE_PREF_FILE = "file";
|
||||
public class PojavProfile {
|
||||
private static final String PROFILE_PREF = "pojav_profile";
|
||||
private static final String PROFILE_PREF_FILE = "file";
|
||||
public static String PROFILE_PREF_TEMP_CONTENT = "tempContent";
|
||||
|
||||
public static SharedPreferences getPrefs(Context ctx) {
|
||||
|
|
@ -25,34 +25,34 @@ public class PojavProfile
|
|||
public static MinecraftAccount getCurrentProfileContent(Context ctx) throws JsonSyntaxException {
|
||||
MinecraftAccount build = MinecraftAccount.load(getCurrentProfileName(ctx));
|
||||
if (build == null) {
|
||||
System.out.println("isTempProfile null? " + (getTempProfileContent(ctx) == null));
|
||||
return getTempProfileContent(ctx);
|
||||
System.out.println("isTempProfile null? " + (getTempProfileContent() == null));
|
||||
return getTempProfileContent();
|
||||
}
|
||||
return build;
|
||||
}
|
||||
|
||||
public static MinecraftAccount getTempProfileContent(Context ctx) {
|
||||
public static MinecraftAccount getTempProfileContent() {
|
||||
try {
|
||||
MinecraftAccount acc = MinecraftAccount.parse(Tools.read(Tools.DIR_DATA+"/cache/tempacc.json"));
|
||||
if (acc.accessToken == null) {
|
||||
acc.accessToken = "0";
|
||||
MinecraftAccount account = MinecraftAccount.parse(Tools.read(Tools.DIR_DATA+"/cache/tempacc.json"));
|
||||
if (account.accessToken == null) {
|
||||
account.accessToken = "0";
|
||||
}
|
||||
if (acc.clientToken == null) {
|
||||
acc.clientToken = "0";
|
||||
if (account.clientToken == null) {
|
||||
account.clientToken = "0";
|
||||
}
|
||||
if (acc.profileId == null) {
|
||||
acc.profileId = "00000000-0000-0000-0000-000000000000";
|
||||
if (account.profileId == null) {
|
||||
account.profileId = "00000000-0000-0000-0000-000000000000";
|
||||
}
|
||||
if (acc.username == null) {
|
||||
acc.username = "0";
|
||||
if (account.username == null) {
|
||||
account.username = "0";
|
||||
}
|
||||
if (acc.selectedVersion == null) {
|
||||
acc.selectedVersion = "1.7.10";
|
||||
if (account.selectedVersion == null) {
|
||||
account.selectedVersion = "1.7.10";
|
||||
}
|
||||
if (acc.msaRefreshToken == null) {
|
||||
acc.msaRefreshToken = "0";
|
||||
if (account.msaRefreshToken == null) {
|
||||
account.msaRefreshToken = "0";
|
||||
}
|
||||
return acc;
|
||||
return account;
|
||||
}catch (IOException e) {
|
||||
Log.e(MinecraftAccount.class.getName(), "Caught an exception while loading the temporary profile",e);
|
||||
return null;
|
||||
|
|
@ -104,7 +104,6 @@ public class PojavProfile
|
|||
Intent intent = new Intent(ctx, PojavLauncherActivity.class); //MCLauncherActivity.class);
|
||||
ctx.startActivity(intent);
|
||||
}
|
||||
|
||||
public static void updateTokens(final Activity ctx, final String name, RefreshListener listen) throws Exception {
|
||||
new RefreshTokenTask(ctx, listen).execute(name);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,8 +3,7 @@ package net.kdt.pojavlaunch;
|
|||
import android.view.*;
|
||||
import android.view.GestureDetector.*;
|
||||
|
||||
public class SingleTapConfirm extends SimpleOnGestureListener
|
||||
{
|
||||
public class SingleTapConfirm extends SimpleOnGestureListener {
|
||||
@Override
|
||||
public boolean onSingleTapUp(MotionEvent event) {
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,121 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import static android.view.MotionEvent.ACTION_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
|
||||
import static android.view.MotionEvent.ACTION_POINTER_UP;
|
||||
import static android.view.MotionEvent.ACTION_UP;
|
||||
|
||||
/**
|
||||
* Class aiming at better detecting X-tap events regardless of the POINTERS
|
||||
* Only uses the least amount of events possible,
|
||||
* since we aren't guaranteed to have all events in order
|
||||
*/
|
||||
public class TapDetector {
|
||||
|
||||
public final static int DETECTION_METHOD_DOWN = 0x1;
|
||||
public final static int DETECTION_METHOD_UP = 0x2;
|
||||
public final static int DETECTION_METHOD_BOTH = 0x3; //Unused for now
|
||||
|
||||
private final static int TAP_MIN_DELTA_MS = 10;
|
||||
private final static int TAP_MAX_DELTA_MS = 300;
|
||||
private final static int TAP_SLOP_SQUARE_PX = (int) Math.pow(Tools.dpToPx(100), 2);
|
||||
|
||||
private final int mTapNumberToDetect;
|
||||
private int mCurrentTapNumber = 0;
|
||||
|
||||
private final int mDetectionMethod;
|
||||
|
||||
private long mLastEventTime = 0;
|
||||
private float mLastX = 9999;
|
||||
private float mLastY = 9999;
|
||||
|
||||
/**
|
||||
* @param tapNumberToDetect How many taps are needed before onTouchEvent returns True.
|
||||
* @param detectionMethod Method used to detect touches. See DETECTION_METHOD constants above.
|
||||
*/
|
||||
public TapDetector(int tapNumberToDetect, int detectionMethod){
|
||||
this.mDetectionMethod = detectionMethod;
|
||||
//We expect both ACTION_DOWN and ACTION_UP for the DETECTION_METHOD_BOTH
|
||||
this.mTapNumberToDetect = detectBothTouch() ? 2*tapNumberToDetect : tapNumberToDetect;
|
||||
}
|
||||
|
||||
/**
|
||||
* A function to call when you have a touch event.
|
||||
* @param e The MotionEvent to inspect
|
||||
* @return whether or not a X-tap happened for a pointer
|
||||
*/
|
||||
public boolean onTouchEvent(MotionEvent e){
|
||||
int eventAction = e.getActionMasked();
|
||||
int pointerIndex = -1;
|
||||
|
||||
//Get the event to look forward
|
||||
if(detectDownTouch()){
|
||||
if(eventAction == ACTION_DOWN) pointerIndex = 0;
|
||||
else if(eventAction == ACTION_POINTER_DOWN) pointerIndex = e.getActionIndex();
|
||||
}
|
||||
if(detectUpTouch()){
|
||||
if(eventAction == ACTION_UP) pointerIndex = 0;
|
||||
else if(eventAction == ACTION_POINTER_UP) pointerIndex = e.getActionIndex();
|
||||
}
|
||||
|
||||
if(pointerIndex == -1) return false; // Useless event
|
||||
|
||||
//Store current event info
|
||||
float eventX = e.getX(pointerIndex);
|
||||
float eventY = e.getY(pointerIndex);
|
||||
long eventTime = e.getEventTime();
|
||||
|
||||
//Compute deltas
|
||||
long deltaTime = eventTime - mLastEventTime;
|
||||
int deltaX = (int) mLastX - (int) eventX;
|
||||
int deltaY = (int) mLastY - (int) eventY;
|
||||
|
||||
//Store current event info to persist on next event
|
||||
mLastEventTime = eventTime;
|
||||
mLastX = eventX;
|
||||
mLastY = eventY;
|
||||
|
||||
//Check for high enough speed and precision
|
||||
if(mCurrentTapNumber > 0){
|
||||
if ((deltaTime < TAP_MIN_DELTA_MS || deltaTime > TAP_MAX_DELTA_MS) ||
|
||||
((deltaX*deltaX + deltaY*deltaY) > TAP_SLOP_SQUARE_PX)) {
|
||||
// We invalidate previous taps, not this one though
|
||||
mCurrentTapNumber = 0;
|
||||
}
|
||||
}
|
||||
|
||||
//A worthy tap happened
|
||||
mCurrentTapNumber += 1;
|
||||
if(mCurrentTapNumber >= mTapNumberToDetect){
|
||||
resetTapDetectionState();
|
||||
return true;
|
||||
}
|
||||
|
||||
//If not enough taps are reached
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the double tap values.
|
||||
*/
|
||||
private void resetTapDetectionState(){
|
||||
mCurrentTapNumber = 0;
|
||||
mLastEventTime = 0;
|
||||
mLastX = 9999;
|
||||
mLastY = 9999;
|
||||
}
|
||||
|
||||
private boolean detectDownTouch(){
|
||||
return (mDetectionMethod & DETECTION_METHOD_DOWN) == DETECTION_METHOD_DOWN;
|
||||
}
|
||||
|
||||
private boolean detectUpTouch(){
|
||||
return (mDetectionMethod & DETECTION_METHOD_UP) == DETECTION_METHOD_UP;
|
||||
}
|
||||
|
||||
private boolean detectBothTouch(){
|
||||
return mDetectionMethod == DETECTION_METHOD_BOTH;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@ package net.kdt.pojavlaunch;
|
|||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.database.Cursor;
|
||||
import android.net.*;
|
||||
import android.os.*;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.system.*;
|
||||
import android.util.*;
|
||||
import com.google.gson.*;
|
||||
|
|
@ -21,6 +23,7 @@ import net.kdt.pojavlaunch.prefs.*;
|
|||
import net.kdt.pojavlaunch.utils.*;
|
||||
import net.kdt.pojavlaunch.value.*;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.LauncherProfiles;
|
||||
import net.kdt.pojavlaunch.value.launcherprofiles.MinecraftProfile;
|
||||
|
||||
import org.apache.commons.codec.binary.Hex;
|
||||
import org.apache.commons.codec.digest.DigestUtils;
|
||||
|
|
@ -48,6 +51,7 @@ public final class Tools {
|
|||
public static String MULTIRT_HOME;
|
||||
public static String LOCAL_RENDERER = null;
|
||||
public static int DEVICE_ARCHITECTURE;
|
||||
public static String LAUNCHERPROFILES_RTPREFIX = "pojav://";
|
||||
|
||||
// New since 3.3.1
|
||||
public static String DIR_ACCOUNT_NEW;
|
||||
|
|
@ -97,14 +101,14 @@ public final class Tools {
|
|||
}
|
||||
|
||||
|
||||
public static void launchMinecraft(final LoggableActivity ctx, MinecraftAccount profile, String versionName) throws Throwable {
|
||||
public static void launchMinecraft(final Activity activity, MinecraftAccount profile, String versionName) throws Throwable {
|
||||
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
|
||||
((ActivityManager)ctx.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(mi);
|
||||
((ActivityManager)activity.getSystemService(Context.ACTIVITY_SERVICE)).getMemoryInfo(mi);
|
||||
if(LauncherPreferences.PREF_RAM_ALLOCATION > (mi.availMem/1048576L)) {
|
||||
Object memoryErrorLock = new Object();
|
||||
ctx.runOnUiThread(() -> {
|
||||
androidx.appcompat.app.AlertDialog.Builder b = new androidx.appcompat.app.AlertDialog.Builder(ctx)
|
||||
.setMessage(ctx.getString(R.string.memory_warning_msg,(mi.availMem/1048576L),LauncherPreferences.PREF_RAM_ALLOCATION))
|
||||
activity.runOnUiThread(() -> {
|
||||
androidx.appcompat.app.AlertDialog.Builder b = new androidx.appcompat.app.AlertDialog.Builder(activity)
|
||||
.setMessage(activity.getString(R.string.memory_warning_msg,(mi.availMem/1048576L),LauncherPreferences.PREF_RAM_ALLOCATION))
|
||||
.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> {synchronized(memoryErrorLock){memoryErrorLock.notifyAll();}})
|
||||
.setOnCancelListener((i) -> {synchronized(memoryErrorLock){memoryErrorLock.notifyAll();}});
|
||||
b.show();
|
||||
|
|
@ -115,16 +119,22 @@ public final class Tools {
|
|||
}
|
||||
|
||||
JMinecraftVersionList.Version versionInfo = Tools.getVersionInfo(null,versionName);
|
||||
PerVersionConfig.update();
|
||||
PerVersionConfig.VersionConfig pvcConfig = PerVersionConfig.configMap.get(versionName);
|
||||
|
||||
String gamedirPath;
|
||||
if(pvcConfig != null && pvcConfig.gamePath != null && !pvcConfig.gamePath.isEmpty()) gamedirPath = pvcConfig.gamePath;
|
||||
else gamedirPath = Tools.DIR_GAME_NEW;
|
||||
if(pvcConfig != null && pvcConfig.jvmArgs != null && !pvcConfig.jvmArgs.isEmpty()) LauncherPreferences.PREF_CUSTOM_JAVA_ARGS = pvcConfig.jvmArgs;
|
||||
String gamedirPath = Tools.DIR_GAME_NEW;
|
||||
if(activity instanceof BaseMainActivity) {
|
||||
LauncherProfiles.update();
|
||||
MinecraftProfile minecraftProfile = ((BaseMainActivity)activity).minecraftProfile;
|
||||
if(minecraftProfile == null) throw new Exception("Launching empty Profile");
|
||||
if(minecraftProfile.gameDir != null && minecraftProfile.gameDir.startsWith(Tools.LAUNCHERPROFILES_RTPREFIX))
|
||||
gamedirPath = minecraftProfile.gameDir.replace(Tools.LAUNCHERPROFILES_RTPREFIX,Tools.DIR_GAME_HOME+"/");
|
||||
if(minecraftProfile.javaArgs != null && !minecraftProfile.javaArgs.isEmpty())
|
||||
LauncherPreferences.PREF_CUSTOM_JAVA_ARGS = minecraftProfile.javaArgs;
|
||||
}
|
||||
PojavLoginActivity.disableSplash(gamedirPath);
|
||||
String[] launchArgs = getMinecraftClientArgs(profile, versionInfo, gamedirPath);
|
||||
|
||||
// Select the appropriate openGL version
|
||||
OldVersionsUtils.selectOpenGlVersion(versionInfo);
|
||||
|
||||
// ctx.appendlnToLog("Minecraft Args: " + Arrays.toString(launchArgs));
|
||||
|
||||
String launchClassPath = generateLaunchClassPath(versionInfo,versionName);
|
||||
|
|
@ -132,8 +142,11 @@ public final class Tools {
|
|||
List<String> javaArgList = new ArrayList<String>();
|
||||
|
||||
// Only Java 8 supports headful AWT for now
|
||||
if (ctx.jreReleaseList.get("JAVA_VERSION").equals("1.8.0")) {
|
||||
if (JREUtils.jreReleaseList.get("JAVA_VERSION").equals("1.8.0")) {
|
||||
getCacioJavaArgs(javaArgList, false);
|
||||
} else if (LauncherPreferences.PREF_ARC_CAPES) {
|
||||
// Opens the java.net package to Arc DNS injector on Java 9+
|
||||
javaArgList.add("--add-opens=java.base/java.net=ALL-UNNAMED");
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -149,6 +162,9 @@ public final class Tools {
|
|||
}
|
||||
*/
|
||||
|
||||
if (versionInfo.logging != null) {
|
||||
javaArgList.add("-Dlog4j.configurationFile=" + Tools.DIR_GAME_NEW + "/" + versionInfo.logging.client.file.id);
|
||||
}
|
||||
javaArgList.addAll(Arrays.asList(getMinecraftJVMArgs(versionName, gamedirPath)));
|
||||
javaArgList.add("-cp");
|
||||
javaArgList.add(getLWJGL3ClassPath() + ":" + launchClassPath);
|
||||
|
|
@ -156,13 +172,13 @@ public final class Tools {
|
|||
javaArgList.add(versionInfo.mainClass);
|
||||
javaArgList.addAll(Arrays.asList(launchArgs));
|
||||
// ctx.appendlnToLog("full args: "+javaArgList.toString());
|
||||
JREUtils.launchJavaVM(ctx, javaArgList);
|
||||
JREUtils.launchJavaVM(activity, javaArgList);
|
||||
}
|
||||
|
||||
public static void getCacioJavaArgs(List<String> javaArgList, boolean isHeadless) {
|
||||
javaArgList.add("-Djava.awt.headless="+isHeadless);
|
||||
// Caciocavallo config AWT-enabled version
|
||||
javaArgList.add("-Dcacio.managed.screensize=" + CallbackBridge.physicalWidth + "x" + CallbackBridge.physicalHeight);
|
||||
javaArgList.add("-Dcacio.managed.screensize=" + AWTCanvasView.AWT_CANVAS_WIDTH + "x" + AWTCanvasView.AWT_CANVAS_HEIGHT);
|
||||
// javaArgList.add("-Dcacio.font.fontmanager=net.java.openjdk.cacio.ctc.CTCFontManager");
|
||||
javaArgList.add("-Dcacio.font.fontmanager=sun.awt.X11FontManager");
|
||||
javaArgList.add("-Dcacio.font.fontscaler=sun.font.FreetypeFontScaler");
|
||||
|
|
@ -271,14 +287,17 @@ public final class Tools {
|
|||
}
|
||||
}
|
||||
}
|
||||
/*
|
||||
minecraftArgs.add("--width");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowWidth));
|
||||
minecraftArgs.add("--height");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowHeight));
|
||||
|
||||
minecraftArgs.add("--fullscreenWidth");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowWidth));
|
||||
minecraftArgs.add("--fullscreenHeight");
|
||||
minecraftArgs.add(Integer.toString(CallbackBridge.windowHeight));
|
||||
*/
|
||||
|
||||
String[] argsFromJson = JSONUtils.insertJSONValueList(
|
||||
splitAndFilterEmpty(
|
||||
|
|
@ -308,7 +327,7 @@ public final class Tools {
|
|||
strList.add(arg);
|
||||
}
|
||||
}
|
||||
strList.add("--fullscreen");
|
||||
//strList.add("--fullscreen");
|
||||
return strList.toArray(new String[0]);
|
||||
}
|
||||
|
||||
|
|
@ -378,51 +397,49 @@ public final class Tools {
|
|||
return libStr.toString();
|
||||
}
|
||||
|
||||
public static DisplayMetrics getDisplayMetrics(Activity ctx) {
|
||||
public static DisplayMetrics getDisplayMetrics(Activity activity) {
|
||||
DisplayMetrics displayMetrics = new DisplayMetrics();
|
||||
|
||||
if(SDK_INT >= Build.VERSION_CODES.N && (ctx.isInMultiWindowMode() || ctx.isInPictureInPictureMode())
|
||||
|| PREF_NOTCH_SIZE == -1 ){
|
||||
if(SDK_INT >= Build.VERSION_CODES.N && (activity.isInMultiWindowMode() || activity.isInPictureInPictureMode())){
|
||||
//For devices with free form/split screen, we need window size, not screen size.
|
||||
displayMetrics = ctx.getResources().getDisplayMetrics();
|
||||
displayMetrics = activity.getResources().getDisplayMetrics();
|
||||
}else{
|
||||
if (SDK_INT >= Build.VERSION_CODES.R) {
|
||||
ctx.getDisplay().getRealMetrics(displayMetrics);
|
||||
activity.getDisplay().getRealMetrics(displayMetrics);
|
||||
} else {
|
||||
ctx.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
|
||||
activity.getWindowManager().getDefaultDisplay().getRealMetrics(displayMetrics);
|
||||
}
|
||||
if(!PREF_IGNORE_NOTCH){
|
||||
//Remove notch width when it isn't ignored.
|
||||
displayMetrics.widthPixels -= PREF_NOTCH_SIZE;
|
||||
}
|
||||
}
|
||||
currentDisplayMetrics = displayMetrics;
|
||||
|
||||
return displayMetrics;
|
||||
}
|
||||
|
||||
public static void setFullscreen(Activity act) {
|
||||
final View decorView = act.getWindow().getDecorView();
|
||||
decorView.setOnSystemUiVisibilityChangeListener (new View.OnSystemUiVisibilityChangeListener() {
|
||||
@Override
|
||||
public void onSystemUiVisibilityChange(int visibility) {
|
||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||
decorView.setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
}
|
||||
});
|
||||
public static void setFullscreen(Activity activity) {
|
||||
final View decorView = activity.getWindow().getDecorView();
|
||||
decorView.setOnSystemUiVisibilityChangeListener (visibility -> {
|
||||
if ((visibility & View.SYSTEM_UI_FLAG_FULLSCREEN) == 0) {
|
||||
decorView.setSystemUiVisibility(
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_STABLE
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||
| View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static DisplayMetrics currentDisplayMetrics;
|
||||
|
||||
public static void updateWindowSize(Activity ctx) {
|
||||
currentDisplayMetrics = getDisplayMetrics(ctx);
|
||||
public static void updateWindowSize(Activity activity) {
|
||||
currentDisplayMetrics = getDisplayMetrics(activity);
|
||||
|
||||
CallbackBridge.physicalWidth = (int) (currentDisplayMetrics.widthPixels);
|
||||
CallbackBridge.physicalHeight = (int) (currentDisplayMetrics.heightPixels);
|
||||
CallbackBridge.physicalWidth = currentDisplayMetrics.widthPixels;
|
||||
CallbackBridge.physicalHeight = currentDisplayMetrics.heightPixels;
|
||||
}
|
||||
|
||||
public static float dpToPx(float dp) {
|
||||
|
|
@ -466,42 +483,37 @@ public final class Tools {
|
|||
private static void showError(final Context ctx, final int titleId, final Throwable e, final boolean exitIfOk, final boolean showMore) {
|
||||
e.printStackTrace();
|
||||
|
||||
Runnable runnable = new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
final String errMsg = showMore ? Log.getStackTraceString(e): e.getMessage();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder((Context) ctx)
|
||||
.setTitle(titleId)
|
||||
.setMessage(errMsg)
|
||||
.setPositiveButton(android.R.string.ok, (DialogInterface.OnClickListener) (p1, p2) -> {
|
||||
if(exitIfOk) {
|
||||
if (ctx instanceof BaseMainActivity) {
|
||||
BaseMainActivity.fullyExit();
|
||||
} else if (ctx instanceof Activity) {
|
||||
((Activity) ctx).finish();
|
||||
}
|
||||
Runnable runnable = () -> {
|
||||
final String errMsg = showMore ? Log.getStackTraceString(e): e.getMessage();
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder((Context) ctx)
|
||||
.setTitle(titleId)
|
||||
.setMessage(errMsg)
|
||||
.setPositiveButton(android.R.string.ok, (DialogInterface.OnClickListener) (p1, p2) -> {
|
||||
if(exitIfOk) {
|
||||
if (ctx instanceof BaseMainActivity) {
|
||||
BaseMainActivity.fullyExit();
|
||||
} else if (ctx instanceof Activity) {
|
||||
((Activity) ctx).finish();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(showMore ? R.string.error_show_less : R.string.error_show_more, (DialogInterface.OnClickListener) (p1, p2) -> showError(ctx, titleId, e, exitIfOk, !showMore))
|
||||
.setNeutralButton(android.R.string.copy, (DialogInterface.OnClickListener) (p1, p2) -> {
|
||||
ClipboardManager mgr = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
mgr.setPrimaryClip(ClipData.newPlainText("error", Log.getStackTraceString(e)));
|
||||
if(exitIfOk) {
|
||||
if (ctx instanceof BaseMainActivity) {
|
||||
BaseMainActivity.fullyExit();
|
||||
} else {
|
||||
((Activity) ctx).finish();
|
||||
}
|
||||
}
|
||||
})
|
||||
.setNegativeButton(showMore ? R.string.error_show_less : R.string.error_show_more, (DialogInterface.OnClickListener) (p1, p2) -> showError(ctx, titleId, e, exitIfOk, !showMore))
|
||||
.setNeutralButton(android.R.string.copy, (DialogInterface.OnClickListener) (p1, p2) -> {
|
||||
ClipboardManager mgr = (ClipboardManager) ctx.getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
mgr.setPrimaryClip(ClipData.newPlainText("error", Log.getStackTraceString(e)));
|
||||
if(exitIfOk) {
|
||||
if (ctx instanceof BaseMainActivity) {
|
||||
BaseMainActivity.fullyExit();
|
||||
} else {
|
||||
((Activity) ctx).finish();
|
||||
}
|
||||
})
|
||||
.setCancelable(!exitIfOk);
|
||||
try {
|
||||
builder.show();
|
||||
} catch (Throwable th) {
|
||||
th.printStackTrace();
|
||||
}
|
||||
}
|
||||
})
|
||||
.setCancelable(!exitIfOk);
|
||||
try {
|
||||
builder.show();
|
||||
} catch (Throwable th) {
|
||||
th.printStackTrace();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -512,8 +524,8 @@ public final class Tools {
|
|||
}
|
||||
}
|
||||
|
||||
public static void dialogOnUiThread(final Activity ctx, final CharSequence title, final CharSequence message) {
|
||||
ctx.runOnUiThread(() -> new AlertDialog.Builder(ctx)
|
||||
public static void dialogOnUiThread(final Activity activity, final CharSequence title, final CharSequence message) {
|
||||
activity.runOnUiThread(() -> new AlertDialog.Builder(activity)
|
||||
.setTitle(title)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
|
@ -597,7 +609,7 @@ public final class Tools {
|
|||
}catch(IOException e) {
|
||||
throw new RuntimeException("Can't find the source version for "+ versionName +" (req version="+customVer.inheritsFrom+")");
|
||||
}
|
||||
inheritsVer.inheritsFrom = inheritsVer.id;
|
||||
//inheritsVer.inheritsFrom = inheritsVer.id;
|
||||
insertSafety(inheritsVer, customVer,
|
||||
"assetIndex", "assets", "id",
|
||||
"mainClass", "minecraftArguments",
|
||||
|
|
@ -693,45 +705,46 @@ public final class Tools {
|
|||
}
|
||||
|
||||
public static String convertStream(InputStream inputStream, Charset charset) throws IOException {
|
||||
String out = "";
|
||||
StringBuilder out = new StringBuilder();
|
||||
int len;
|
||||
byte[] buf = new byte[512];
|
||||
while((len = inputStream.read(buf))!=-1) {
|
||||
out += new String(buf,0,len,charset);
|
||||
out.append(new String(buf, 0, len, charset));
|
||||
}
|
||||
return out;
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public static File lastFileModified(String dir) {
|
||||
File fl = new File(dir);
|
||||
|
||||
File[] files = fl.listFiles(new FileFilter() {
|
||||
public boolean accept(File file) {
|
||||
return file.isFile();
|
||||
}
|
||||
});
|
||||
|
||||
File[] files = fl.listFiles(File::isFile);
|
||||
if(files == null) {
|
||||
return null;
|
||||
// The patch was a bit wrong...
|
||||
// So, this may be null, why? Because this folder may not exist yet
|
||||
// Or it may not have any files...
|
||||
// Doesn't matter. We must check for that in the crash fragment.
|
||||
}
|
||||
long lastMod = Long.MIN_VALUE;
|
||||
File choice = null;
|
||||
for (File file : files) {
|
||||
if (file.lastModified() > lastMod) {
|
||||
choice = file;
|
||||
lastMod = file.lastModified();
|
||||
if (file.lastModified() > lastMod) {
|
||||
choice = file;
|
||||
lastMod = file.lastModified();
|
||||
}
|
||||
}
|
||||
|
||||
return choice;
|
||||
}
|
||||
|
||||
|
||||
public static String read(InputStream is) throws IOException {
|
||||
String out = "";
|
||||
StringBuilder out = new StringBuilder();
|
||||
int len;
|
||||
byte[] buf = new byte[512];
|
||||
while((len = is.read(buf))!=-1) {
|
||||
out += new String(buf,0,len);
|
||||
out.append(new String(buf, 0, len));
|
||||
}
|
||||
return out;
|
||||
return out.toString();
|
||||
}
|
||||
|
||||
public static String read(String path) throws IOException {
|
||||
|
|
@ -777,6 +790,7 @@ public final class Tools {
|
|||
public abstract static class DownloaderFeedback {
|
||||
public abstract void updateProgress(int curr, int max);
|
||||
}
|
||||
|
||||
public static void downloadFileMonitored(String urlInput,String nameOutput, DownloaderFeedback monitor) throws IOException {
|
||||
File nameOutputFile = new File(nameOutput);
|
||||
if (!nameOutputFile.exists()) {
|
||||
|
|
@ -813,75 +827,6 @@ public final class Tools {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
public static class ZipTool
|
||||
{
|
||||
private ZipTool(){}
|
||||
public static void zip(List<File> files, File zipFile) throws IOException {
|
||||
final int BUFFER_SIZE = 2048;
|
||||
|
||||
BufferedInputStream origin = null;
|
||||
ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream(zipFile)));
|
||||
|
||||
try {
|
||||
byte data[] = new byte[BUFFER_SIZE];
|
||||
|
||||
for (File file : files) {
|
||||
FileInputStream fileInputStream = new FileInputStream( file );
|
||||
|
||||
origin = new BufferedInputStream(fileInputStream, BUFFER_SIZE);
|
||||
|
||||
try {
|
||||
ZipEntry entry = new ZipEntry(file.getName());
|
||||
|
||||
out.putNextEntry(entry);
|
||||
|
||||
int count;
|
||||
while ((count = origin.read(data, 0, BUFFER_SIZE)) != -1) {
|
||||
out.write(data, 0, count);
|
||||
}
|
||||
}
|
||||
finally {
|
||||
origin.close();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
public static void unzip(File zipFile, File targetDirectory) throws IOException {
|
||||
final int BUFFER_SIZE = 1024;
|
||||
ZipInputStream zis = new ZipInputStream(
|
||||
new BufferedInputStream(new FileInputStream(zipFile)));
|
||||
try {
|
||||
ZipEntry ze;
|
||||
int count;
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
while ((ze = zis.getNextEntry()) != null) {
|
||||
File file = new File(targetDirectory, ze.getName());
|
||||
File dir = ze.isDirectory() ? file : file.getParentFile();
|
||||
if (!dir.isDirectory() && !dir.mkdirs())
|
||||
throw new FileNotFoundException("Failed to ensure directory: " +
|
||||
dir.getAbsolutePath());
|
||||
if (ze.isDirectory())
|
||||
continue;
|
||||
FileOutputStream fout = new FileOutputStream(file);
|
||||
try {
|
||||
while ((count = zis.read(buffer)) != -1)
|
||||
fout.write(buffer, 0, count);
|
||||
} finally {
|
||||
fout.close();
|
||||
}
|
||||
/* if time should be restored as well
|
||||
long time = ze.getTime();
|
||||
if (time > 0)
|
||||
file.setLastModified(time);
|
||||
*/
|
||||
}
|
||||
} finally {
|
||||
zis.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void ignoreNotch(boolean shouldIgnore, Activity ctx){
|
||||
if (SDK_INT >= P) {
|
||||
|
|
@ -914,4 +859,26 @@ public final class Tools {
|
|||
if(displaySideRes % 2 != 0) displaySideRes ++;
|
||||
return displaySideRes;
|
||||
}
|
||||
|
||||
public static String getFileName(Context ctx, Uri uri) {
|
||||
String result = null;
|
||||
if (uri.getScheme().equals("content")) {
|
||||
Cursor cursor = ctx.getContentResolver().query(uri, null, null, null, null);
|
||||
try {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
result = cursor.getString(cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME));
|
||||
}
|
||||
} finally {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
if (result == null) {
|
||||
result = uri.getPath();
|
||||
int cut = result.lastIndexOf('/');
|
||||
if (cut != -1) {
|
||||
result = result.substring(cut + 1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,189 @@
|
|||
package net.kdt.pojavlaunch;
|
||||
|
||||
import static net.kdt.pojavlaunch.MinecraftGLSurface.FINGER_SCROLL_THRESHOLD;
|
||||
import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.DEFAULT_PREF;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.GestureDetector;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
/**
|
||||
* Class dealing with the virtual mouse
|
||||
*/
|
||||
public class Touchpad extends FrameLayout {
|
||||
/* Whether the Touchpad should be displayed */
|
||||
private boolean mDisplayState;
|
||||
/* Mouse pointer icon used by the touchpad */
|
||||
private final ImageView mMousePointerImageView = new ImageView(getContext());
|
||||
/* Detect a classic android Tap */
|
||||
private final GestureDetector mSingleTapDetector = new GestureDetector(getContext(), new SingleTapConfirm());
|
||||
/* Resolution scaler option, allow downsizing a window */
|
||||
private final float mScaleFactor = DEFAULT_PREF.getInt("resolutionRatio",100)/100f;
|
||||
/* Current pointer ID to move the mouse */
|
||||
private int mCurrentPointerID = -1000;
|
||||
/* Previous MotionEvent position, not scale */
|
||||
private float mPrevX, mPrevY;
|
||||
/* Last first pointer positions non-scaled, used to scroll distance */
|
||||
private float mScrollLastInitialX, mScrollLastInitialY;
|
||||
|
||||
public Touchpad(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public Touchpad(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
// MotionEvent reports input details from the touch screen
|
||||
// and other input controls. In this case, you are only
|
||||
// interested in events where the touch position changed.
|
||||
// int index = event.getActionIndex();
|
||||
if(CallbackBridge.isGrabbing()) {
|
||||
disable();
|
||||
return false;
|
||||
}
|
||||
int action = event.getActionMasked();
|
||||
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
float mouseX = mMousePointerImageView.getX();
|
||||
float mouseY = mMousePointerImageView.getY();
|
||||
|
||||
if (mSingleTapDetector.onTouchEvent(event)) {
|
||||
CallbackBridge.mouseX = (mouseX * mScaleFactor);
|
||||
CallbackBridge.mouseY = (mouseY * mScaleFactor);
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
CallbackBridge.sendMouseKeycode(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
switch (action) {
|
||||
case MotionEvent.ACTION_POINTER_DOWN: // 5
|
||||
mScrollLastInitialX = event.getX();
|
||||
mScrollLastInitialY = event.getY();
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_DOWN:
|
||||
mPrevX = x;
|
||||
mPrevY = y;
|
||||
mCurrentPointerID = event.getPointerId(0);
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_MOVE: // 2
|
||||
//Scrolling feature
|
||||
if (!LauncherPreferences.PREF_DISABLE_GESTURES && !CallbackBridge.isGrabbing() && event.getPointerCount() >= 2) {
|
||||
int hScroll = ((int) (event.getX() - mScrollLastInitialX)) / FINGER_SCROLL_THRESHOLD;
|
||||
int vScroll = ((int) (event.getY() - mScrollLastInitialY)) / FINGER_SCROLL_THRESHOLD;
|
||||
|
||||
if(vScroll != 0 || hScroll != 0){
|
||||
CallbackBridge.sendScroll(hScroll, vScroll);
|
||||
mScrollLastInitialX = event.getX();
|
||||
mScrollLastInitialY = event.getY();
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Mouse movement
|
||||
if(mCurrentPointerID == event.getPointerId(0)) {
|
||||
mouseX = Math.max(0, Math.min(currentDisplayMetrics.widthPixels, mouseX + (x - mPrevX) * LauncherPreferences.PREF_MOUSESPEED));
|
||||
mouseY = Math.max(0, Math.min(currentDisplayMetrics.heightPixels, mouseY + (y - mPrevY) * LauncherPreferences.PREF_MOUSESPEED));
|
||||
|
||||
placeMouseAt(mouseX, mouseY);
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
}else mCurrentPointerID = event.getPointerId(0);
|
||||
|
||||
mPrevX = x;
|
||||
mPrevY = y;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
mPrevX = x;
|
||||
mPrevY = y;
|
||||
mCurrentPointerID = -1000;
|
||||
break;
|
||||
}
|
||||
|
||||
//debugText.setText(CallbackBridge.DEBUG_STRING.toString());
|
||||
CallbackBridge.DEBUG_STRING.setLength(0);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Enable the touchpad */
|
||||
public void enable(){
|
||||
setVisibility(VISIBLE);
|
||||
placeMouseAt(currentDisplayMetrics.widthPixels / 2, currentDisplayMetrics.heightPixels / 2);
|
||||
}
|
||||
|
||||
/** Disable the touchpad and hides the mouse */
|
||||
public void disable(){
|
||||
setVisibility(GONE);
|
||||
}
|
||||
|
||||
/** @return The new state, enabled or disabled */
|
||||
public boolean switchState(){
|
||||
mDisplayState = !mDisplayState;
|
||||
return mDisplayState;
|
||||
}
|
||||
|
||||
public void placeMouseAt(float x, float y) {
|
||||
mMousePointerImageView.setX(x);
|
||||
mMousePointerImageView.setY(y);
|
||||
CallbackBridge.mouseX = (x * mScaleFactor);
|
||||
CallbackBridge.mouseY = (y * mScaleFactor);
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
}
|
||||
|
||||
private void init(){
|
||||
// Setup mouse pointer
|
||||
mMousePointerImageView.setImageDrawable(ResourcesCompat.getDrawable(getResources(), R.drawable.ic_mouse_pointer, getContext().getTheme()));
|
||||
mMousePointerImageView.post(() -> {
|
||||
ViewGroup.LayoutParams params = mMousePointerImageView.getLayoutParams();
|
||||
params.width = (int) (36 / 100f * LauncherPreferences.PREF_MOUSESCALE);
|
||||
params.height = (int) (54 / 100f * LauncherPreferences.PREF_MOUSESCALE);
|
||||
});
|
||||
addView(mMousePointerImageView);
|
||||
setFocusable(false);
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
setDefaultFocusHighlightEnabled(false);
|
||||
}
|
||||
|
||||
// When the game is grabbing, we should not display the mouse
|
||||
disable();
|
||||
mDisplayState = false;
|
||||
Thread virtualMouseGrabThread = new Thread(() -> {
|
||||
while (true) {
|
||||
if (!CallbackBridge.isGrabbing() && mDisplayState && getVisibility() != VISIBLE) {
|
||||
post(this::enable);
|
||||
}else{
|
||||
if ((CallbackBridge.isGrabbing() && getVisibility() != View.GONE) || !mDisplayState && getVisibility() == VISIBLE) {
|
||||
post(this::disable);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}, "VirtualMouseGrabThread");
|
||||
virtualMouseGrabThread.setPriority(Thread.MIN_PRIORITY);
|
||||
virtualMouseGrabThread.start();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -71,7 +71,7 @@ public class MicrosoftAuthTask extends AsyncTask<String, Void, Object> {
|
|||
*/
|
||||
Msa msa = new Msa(this, Boolean.parseBoolean(args[0]), args[1]);
|
||||
|
||||
MinecraftAccount acc = new MinecraftAccount();
|
||||
MinecraftAccount acc = MinecraftAccount.load(msa.mcName);
|
||||
if (msa.doesOwnGame) {
|
||||
acc.clientToken = "0"; /* FIXME */
|
||||
acc.accessToken = msa.mcToken;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import android.net.Uri;
|
|||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.ViewGroup;
|
||||
import android.webkit.CookieManager;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebSettings;
|
||||
import android.webkit.WebView;
|
||||
|
|
@ -24,6 +25,7 @@ public class MicrosoftLoginGUIActivity extends AppCompatActivity {
|
|||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
CookieManager.getInstance().removeAllCookie();
|
||||
waitDialog = new ProgressDialog(this);
|
||||
waitDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
|
||||
waitDialog.setIndeterminate(true);
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
|
||||
package net.kdt.pojavlaunch.authenticator.mojang.yggdrasil;
|
||||
|
||||
import java.util.UUID;
|
||||
|
|
@ -20,5 +21,4 @@ public class AuthenticateRequest {
|
|||
this.agent.name = clientName;
|
||||
this.agent.version = clientVersion;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,6 @@ package net.kdt.pojavlaunch.authenticator.mojang;
|
|||
|
||||
public interface LoginListener
|
||||
{
|
||||
public void onBeforeLogin();
|
||||
public void onLoginDone(String[] result);
|
||||
}
|
||||
public void onBeforeLogin();
|
||||
public void onLoginDone(String[] result);
|
||||
}
|
||||
|
|
@ -11,23 +11,23 @@ public class LoginTask extends AsyncTask<String, Void, Void>
|
|||
private YggdrasilAuthenticator authenticator = new YggdrasilAuthenticator();
|
||||
//private String TAG = "MojangAuth-login";
|
||||
private LoginListener listener;
|
||||
|
||||
|
||||
public LoginTask setLoginListener(LoginListener listener) {
|
||||
this.listener = listener;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
private UUID getRandomUUID() {
|
||||
return UUID.randomUUID();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
listener.onBeforeLogin();
|
||||
|
||||
|
||||
super.onPreExecute();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(String[] args) {
|
||||
ArrayList<String> str = new ArrayList<String>();
|
||||
|
|
@ -49,7 +49,7 @@ public class LoginTask extends AsyncTask<String, Void, Void>
|
|||
}
|
||||
}
|
||||
}
|
||||
//MainActivity.updateStatus(804);
|
||||
//MainActivity.updateStatus(804);
|
||||
catch(Throwable e){
|
||||
str.add(e.getMessage());
|
||||
}
|
||||
|
|
@ -57,15 +57,15 @@ public class LoginTask extends AsyncTask<String, Void, Void>
|
|||
catch(Exception e){
|
||||
str.add(e.getMessage());
|
||||
}
|
||||
|
||||
|
||||
listener.onLoginDone(str.toArray(new String[0]));
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void result) {
|
||||
// listener.onLoginDone(result);
|
||||
super.onPostExecute(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -52,7 +52,7 @@ public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
|
|||
} else if (response.selectedProfile == null) {
|
||||
throw new IllegalArgumentException("Can't refresh a demo account!");
|
||||
}
|
||||
|
||||
|
||||
profilePath.clientToken = response.clientToken.toString();
|
||||
profilePath.accessToken = response.accessToken;
|
||||
profilePath.username = response.selectedProfile.name;
|
||||
|
|
@ -75,5 +75,4 @@ public class RefreshTokenTask extends AsyncTask<String, Void, Throwable> {
|
|||
listener.onFailed(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -7,5 +7,4 @@ public class AuthenticateResponse {
|
|||
public Profile[] availableProfiles;
|
||||
public UUID clientToken;
|
||||
public Profile selectedProfile;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -6,5 +6,4 @@ public class RefreshResponse {
|
|||
public String accessToken;
|
||||
public UUID clientToken;
|
||||
public Profile selectedProfile;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -89,9 +89,8 @@ public class YggdrasilAuthenticator {
|
|||
throw th;
|
||||
}
|
||||
}
|
||||
|
||||
public AuthenticateResponse authenticate(String username, String password, UUID clientId) throws IOException, Throwable {
|
||||
NetworkResponse obj = makeRequest("authenticate", new AuthenticateRequest(username, password, clientId, this.clientName, this.clientVersion), AuthenticateResponse.class);
|
||||
NetworkResponse obj = makeRequest("authenticate", new net.kdt.pojavlaunch.authenticator.mojang.yggdrasil.AuthenticateRequest(username, password, clientId, this.clientName, this.clientVersion), AuthenticateResponse.class);
|
||||
/*
|
||||
if (obj.statusCode != 200) {
|
||||
throw new RuntimeException("Invalid username or password, status code: " + obj.statusCode);
|
||||
|
|
@ -110,11 +109,12 @@ public class YggdrasilAuthenticator {
|
|||
return (RefreshResponse) obj.response;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public NetworkResponse validate(String authToken) throws Throwable {
|
||||
return makeRequest("validate", new RefreshRequest(authToken, null), null);
|
||||
}
|
||||
|
||||
|
||||
public NetworkResponse invalidate(String authToken, UUID clientId) throws Throwable {
|
||||
return makeRequest("invalidate", new RefreshRequest(authToken, clientId), null);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
public interface AlphaSelectionListener {
|
||||
void onAlphaSelected(int alpha);
|
||||
}
|
||||
|
|
@ -0,0 +1,76 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
import top.defaults.checkerboarddrawable.CheckerboardDrawable;
|
||||
|
||||
public class AlphaView extends View {
|
||||
private final Drawable mCheckerboardDrawable = CheckerboardDrawable.create();
|
||||
private final Paint mShaderPaint = new Paint();
|
||||
private final Paint mBlackPaint;
|
||||
private final RectF mViewSize = new RectF(0,0,0,0);
|
||||
private AlphaSelectionListener mAlphaSelectionListener;
|
||||
private int mSelectedAlpha;
|
||||
private float mAlphaDiv; // for quick pos->alpha multiplication
|
||||
private float mScreenDiv; // for quick alpha->pos multiplication
|
||||
private float mHeightThird; // 1/3 of the view size for cursor
|
||||
public AlphaView(Context ctx, AttributeSet attrs) {
|
||||
super(ctx,attrs);
|
||||
mBlackPaint = new Paint();
|
||||
mBlackPaint.setStrokeWidth(Tools.dpToPx(3));
|
||||
mBlackPaint.setColor(Color.BLACK);
|
||||
}
|
||||
|
||||
public void setAlphaSelectionListener(AlphaSelectionListener alphaSelectionListener) {
|
||||
mAlphaSelectionListener = alphaSelectionListener;
|
||||
}
|
||||
|
||||
public void setAlpha(int alpha) {
|
||||
mSelectedAlpha = alpha;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
mSelectedAlpha = (int) (mAlphaDiv * event.getX());
|
||||
if(mSelectedAlpha < 0) mSelectedAlpha = 0;
|
||||
if(mSelectedAlpha > 0xff) mSelectedAlpha = 0xff;
|
||||
if(mAlphaSelectionListener != null) mAlphaSelectionListener.onAlphaSelected(mSelectedAlpha);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int old_w, int old_h) {
|
||||
mViewSize.right = w;
|
||||
mViewSize.bottom = h;
|
||||
float h2 = mViewSize.bottom / 2f;
|
||||
mShaderPaint.setShader(new LinearGradient(0,h2,w,h2, 0, Color.BLACK, Shader.TileMode.REPEAT));
|
||||
mAlphaDiv = 255f / mViewSize.right;
|
||||
mScreenDiv = mViewSize.right / 255f;
|
||||
mHeightThird = mViewSize.bottom / 3f;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
mCheckerboardDrawable.draw(canvas);
|
||||
canvas.drawRect(mViewSize, mShaderPaint);
|
||||
float linePos = mSelectedAlpha * mScreenDiv;
|
||||
canvas.drawLine(linePos, 0 ,linePos, mHeightThird, mBlackPaint);
|
||||
canvas.drawLine(linePos, mHeightThird * 2 ,linePos, mViewSize.bottom, mBlackPaint);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
public interface ColorSelectionListener {
|
||||
/**
|
||||
* This method gets called by the ColorSelector when the color is selected
|
||||
* @param color the selected color
|
||||
*/
|
||||
void onColorSelected(int color);
|
||||
}
|
||||
|
|
@ -0,0 +1,150 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.graphics.Color;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
|
||||
public class ColorSelector implements HueSelectionListener, RectangleSelectionListener, AlphaSelectionListener, TextWatcher{
|
||||
private static final int ALPHA_MASK = ~(0xFF << 24);
|
||||
private final HueView mHueView;
|
||||
private final SVRectangleView mLuminosityIntensityView;
|
||||
private final AlphaView mAlphaView;
|
||||
private final ColorSideBySideView mColorView;
|
||||
private final EditText mTextView;
|
||||
private final AlertDialog mDialog;
|
||||
private ColorSelectionListener mColorSelectionListener;
|
||||
private float[] mHueTemplate = new float[] {0,1,1};
|
||||
private float[] mHsvSelected = new float[] {360,1,1};
|
||||
private int mAlphaSelected = 0xff;
|
||||
private ColorStateList mTextColors;
|
||||
private boolean mWatch = true;
|
||||
|
||||
|
||||
/**
|
||||
* Creates a color selector dialog for this Context.
|
||||
* @param context Context used for this ColorSelector dialog
|
||||
* @param colorSelectionListener Color selection listener to which the events will be sent to. Can be null.
|
||||
*/
|
||||
public ColorSelector(Context context, ColorSelectionListener colorSelectionListener) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(context);
|
||||
View view = LayoutInflater.from(context).inflate(R.layout.dialog_color_selector,null);
|
||||
mHueView = view.findViewById(R.id.color_selector_hue_view);
|
||||
mLuminosityIntensityView = view.findViewById(R.id.color_selector_rectangle_view);
|
||||
mAlphaView = view.findViewById(R.id.color_selector_alpha_view);
|
||||
mColorView = view.findViewById(R.id.color_selector_color_view);
|
||||
mTextView = view.findViewById(R.id.color_selector_hex_edit);
|
||||
runColor(Color.RED);
|
||||
mHueView.setHueSelectionListener(this);
|
||||
mLuminosityIntensityView.setRectSelectionListener(this);
|
||||
mAlphaView.setAlphaSelectionListener(this);
|
||||
mTextView.addTextChangedListener(this);
|
||||
mTextColors = mTextView.getTextColors();
|
||||
builder.setView(view);
|
||||
builder.setPositiveButton(android.R.string.ok,(dialog,which)->{
|
||||
if (mColorSelectionListener != null) {
|
||||
mColorSelectionListener.onColorSelected(Color.HSVToColor(mAlphaSelected, mHsvSelected));
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, ((dialog, which) -> {}));
|
||||
mColorSelectionListener = colorSelectionListener;
|
||||
mDialog = builder.create();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the color selector with the default (red) color selected.
|
||||
*/
|
||||
public void show() {
|
||||
runColor(Color.RED);
|
||||
dispatchColorChange();
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the color selector with the desired ARGB color selected
|
||||
* @param previousColor the desired ARGB color
|
||||
*/
|
||||
public void show(int previousColor) {
|
||||
runColor(previousColor); // initialize
|
||||
dispatchColorChange(); // set the hex text
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onHueSelected(float hue) {
|
||||
mHsvSelected[0] = mHueTemplate[0] = hue;
|
||||
mLuminosityIntensityView.setColor(Color.HSVToColor(mHueTemplate), true);
|
||||
dispatchColorChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLuminosityIntensityChanged(float luminosity, float intensity) {
|
||||
mHsvSelected[1] = intensity;
|
||||
mHsvSelected[2] = luminosity;
|
||||
dispatchColorChange();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAlphaSelected(int alpha) {
|
||||
mAlphaSelected = alpha;
|
||||
dispatchColorChange();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the alpha value of the color passed in, and returns the result.
|
||||
* @param color the color to replace the alpha of
|
||||
* @param alpha the alpha to use
|
||||
* @return the new color
|
||||
*/
|
||||
public static int setAlpha(int color, int alpha) {
|
||||
return color & ALPHA_MASK | ((alpha & 0xFF) << 24);
|
||||
}
|
||||
|
||||
//IUO: called on all color changes
|
||||
protected void dispatchColorChange() {
|
||||
int color = Color.HSVToColor(mAlphaSelected, mHsvSelected);
|
||||
mColorView.setColor(color);
|
||||
mWatch = false;
|
||||
mTextView.setText(String.format("%08X",color));
|
||||
}
|
||||
|
||||
//IUO: sets all Views to render the desired color. Used for initilaization and HEX color input
|
||||
protected void runColor(int color) {
|
||||
Color.RGBToHSV(Color.red(color), Color.green(color), Color.blue(color), mHsvSelected);
|
||||
mHueTemplate[0] = mHsvSelected[0];
|
||||
mHueView.setHue(mHsvSelected[0]);
|
||||
mLuminosityIntensityView.setColor(Color.HSVToColor(mHueTemplate), false);
|
||||
mLuminosityIntensityView.setLuminosityIntensity(mHsvSelected[2], mHsvSelected[1]);
|
||||
mAlphaSelected = Color.alpha(color);
|
||||
mAlphaView.setAlpha(mAlphaSelected);
|
||||
mColorView.setColor(color);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable s) {
|
||||
if(mWatch) {
|
||||
try {
|
||||
int color = Integer.parseInt(s.toString(), 16);
|
||||
mTextView.setTextColor(mTextColors);
|
||||
runColor(color);
|
||||
}catch (NumberFormatException exception) {
|
||||
mTextView.setTextColor(Color.RED);
|
||||
}
|
||||
}else{
|
||||
mWatch = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import top.defaults.checkerboarddrawable.CheckerboardDrawable;
|
||||
|
||||
public class ColorSideBySideView extends View {
|
||||
private final Paint mPaint;
|
||||
private final CheckerboardDrawable mCheckerboardDrawable = CheckerboardDrawable.create();
|
||||
private int mColor;
|
||||
private int mAlphaColor;
|
||||
private float mWidth;
|
||||
private float mHeight;
|
||||
private float mHalfHeight;
|
||||
public ColorSideBySideView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mPaint = new Paint();
|
||||
}
|
||||
|
||||
public void setColor(int color) {
|
||||
mColor = ColorSelector.setAlpha(color, 0xff);
|
||||
mAlphaColor = color;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
mCheckerboardDrawable.draw(canvas);
|
||||
mPaint.setColor(mColor);
|
||||
canvas.drawRect(0,0,mWidth, mHalfHeight, mPaint);
|
||||
mPaint.setColor(mAlphaColor);
|
||||
canvas.drawRect(0,mHalfHeight,mWidth,mHeight, mPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int old_w, int old_h) {
|
||||
mHalfHeight = h/2f;
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
public interface HueSelectionListener {
|
||||
void onHueSelected(float hue);
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
|
||||
|
||||
public class HueView extends View {
|
||||
private final Paint blackPaint = new Paint();
|
||||
private Bitmap mGamma;
|
||||
private HueSelectionListener mHueSelectionListener;
|
||||
private float mSelectionHue;
|
||||
private float mWidthHueRatio;
|
||||
private float mHueWidthRatio;
|
||||
private float mWidth;
|
||||
private float mHeight;
|
||||
private float mHeightThird;
|
||||
public HueView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
blackPaint.setColor(Color.BLACK);
|
||||
blackPaint.setStrokeWidth(Tools.dpToPx(3));
|
||||
}
|
||||
|
||||
public void setHueSelectionListener(HueSelectionListener listener) {
|
||||
mHueSelectionListener = listener;
|
||||
}
|
||||
public void setHue(float hue) {
|
||||
mSelectionHue = hue;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
mSelectionHue = event.getX() * mWidthHueRatio;
|
||||
invalidate();
|
||||
if(mHueSelectionListener != null) mHueSelectionListener.onHueSelected(mSelectionHue);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
canvas.drawBitmap(mGamma, 0, 0 ,null);
|
||||
float linePos = mSelectionHue * mHueWidthRatio;
|
||||
canvas.drawLine(linePos, 0 ,linePos, mHeightThird, blackPaint);
|
||||
canvas.drawLine(linePos, mHeightThird * 2 ,linePos, mHeight, blackPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int old_w, int old_h) {
|
||||
mWidth = w;
|
||||
mHeight = h;
|
||||
mHeightThird = mHeight / 3;
|
||||
regenerateGammaBitmap();
|
||||
}
|
||||
|
||||
protected void regenerateGammaBitmap() {
|
||||
if(mGamma != null)
|
||||
mGamma.recycle();
|
||||
mGamma = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
|
||||
Paint paint = new Paint();
|
||||
Canvas canvas = new Canvas(mGamma);
|
||||
mWidthHueRatio = 360/ mWidth;
|
||||
mHueWidthRatio = mWidth / 360;
|
||||
float[] hsvFiller = new float[] {0, 1, 1};
|
||||
for(float i = 0; i < mWidth; i++) {
|
||||
hsvFiller[0] = i * mWidthHueRatio;
|
||||
paint.setColor(Color.HSVToColor(hsvFiller));
|
||||
canvas.drawLine(i,0,i, mHeight,paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
public interface RectangleSelectionListener {
|
||||
void onLuminosityIntensityChanged(float luminosity, float intensity);
|
||||
}
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
package net.kdt.pojavlaunch.colorselector;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.LinearGradient;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.Shader;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
public class SVRectangleView extends View {
|
||||
private final Paint mColorPaint = new Paint();
|
||||
private final Paint mPointerPaint = new Paint();
|
||||
private final float mPointerSize;
|
||||
private Bitmap mSvRectangle;
|
||||
private RectF mViewSize;
|
||||
private float mHeightInverted;
|
||||
private float mWidthInverted;
|
||||
private float mFingerPosX;
|
||||
private float mFingerPosY;
|
||||
RectangleSelectionListener mRectSelectionListener;
|
||||
public SVRectangleView(Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mColorPaint.setColor(Color.BLACK);
|
||||
mColorPaint.setStyle(Paint.Style.FILL);
|
||||
mPointerSize = Tools.dpToPx(6);
|
||||
mPointerPaint.setColor(Color.BLACK);
|
||||
mPointerPaint.setStrokeWidth(Tools.dpToPx(3));
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent event) {
|
||||
float x = event.getX();
|
||||
float y = event.getY();
|
||||
mFingerPosX = x * mWidthInverted;
|
||||
mFingerPosY = y * mHeightInverted;
|
||||
|
||||
if(mFingerPosX < 0) mFingerPosX = 0;
|
||||
else if(mFingerPosX > 1) mFingerPosX = 1;
|
||||
|
||||
if(mFingerPosY < 0) mFingerPosY = 0;
|
||||
else if(mFingerPosY > 1) mFingerPosY = 1;
|
||||
|
||||
if(mRectSelectionListener != null) mRectSelectionListener.onLuminosityIntensityChanged(mFingerPosY,mFingerPosX);
|
||||
invalidate();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void setLuminosityIntensity(float luminosity, float intensity) {
|
||||
mFingerPosX = intensity;
|
||||
mFingerPosY = luminosity;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
public void setColor(int color, boolean invalidate) {
|
||||
mColorPaint.setColor(color);
|
||||
if(invalidate) invalidate();
|
||||
}
|
||||
|
||||
public void setRectSelectionListener(RectangleSelectionListener listener) {
|
||||
mRectSelectionListener = listener;
|
||||
}
|
||||
protected void drawPointer(Canvas canvas, float x, float y) {
|
||||
canvas.drawLine(mPointerSize * 2 + x, y, mPointerSize + x, y, mPointerPaint);
|
||||
canvas.drawLine(x - mPointerSize * 2, y, x - mPointerSize, y, mPointerPaint);
|
||||
canvas.drawLine(x, mPointerSize * 2 + y, x, mPointerSize + y, mPointerPaint);
|
||||
canvas.drawLine(x, y - mPointerSize * 2, x, y - mPointerSize, mPointerPaint);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
canvas.drawRect(mViewSize, mColorPaint);
|
||||
canvas.drawBitmap(mSvRectangle, 0,0, null);
|
||||
drawPointer(canvas, mViewSize.right * mFingerPosX, mViewSize.bottom * mFingerPosY);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int old_w, int old_h) {
|
||||
mViewSize = new RectF(0,0, w,h);
|
||||
mWidthInverted = 1/mViewSize.right;
|
||||
mHeightInverted = 1/mViewSize.bottom;
|
||||
if(w > 0 && h > 0)
|
||||
regenerateRectangle();
|
||||
}
|
||||
|
||||
protected void regenerateRectangle() {
|
||||
int w = getWidth();
|
||||
int h = getHeight();
|
||||
if(mSvRectangle != null)
|
||||
mSvRectangle.recycle();
|
||||
mSvRectangle = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
|
||||
Paint rectPaint = new Paint();
|
||||
Canvas canvas = new Canvas(mSvRectangle);
|
||||
float h2f = h/2f;
|
||||
float w2f = w/2f;
|
||||
rectPaint.setShader(new LinearGradient(0,h2f, w, h2f, Color.WHITE,0, Shader.TileMode.CLAMP));
|
||||
canvas.drawRect(mViewSize, rectPaint);
|
||||
rectPaint.setShader(new LinearGradient(w2f,0, w2f, h, Color.BLACK,0, Shader.TileMode.CLAMP));
|
||||
canvas.drawRect(mViewSize, rectPaint);
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,6 @@ package net.kdt.pojavlaunch.customcontrols;
|
|||
import android.util.*;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
|
|
@ -14,8 +13,12 @@ import net.objecthunter.exp4j.function.Function;
|
|||
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
import static net.kdt.pojavlaunch.LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN;
|
||||
import static net.kdt.pojavlaunch.LwjglGlfwKeycode.GLFW_KEY_UNKNOWN;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@Keep
|
||||
public class ControlData {
|
||||
|
||||
public static final int SPECIALBTN_KEYBOARD = -1;
|
||||
|
|
@ -34,10 +37,9 @@ public class ControlData {
|
|||
public boolean isHideable;
|
||||
|
||||
private static WeakReference<ExpressionBuilder> builder = new WeakReference<>(null);
|
||||
private static WeakReference<Field> expression = new WeakReference<>(null);
|
||||
private static WeakReference<ArrayMap<String , String>> conversionMap = new WeakReference<>(null);
|
||||
static {
|
||||
bypassExpressionBuilder();
|
||||
buildExpressionBuilder();
|
||||
buildConversionMap();
|
||||
}
|
||||
|
||||
|
|
@ -174,7 +176,7 @@ public class ControlData {
|
|||
|
||||
public void execute(boolean isDown) {
|
||||
for(int keycode : keycodes){
|
||||
BaseMainActivity.sendKeyPress(keycode, 0, isDown);
|
||||
sendKeyPress(keycode, 0, isDown);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -226,11 +228,9 @@ public class ControlData {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a weak reference to a builder and its expression field.
|
||||
* Although VERY bad practice it isn't slower due to saved GC time.
|
||||
* The normal way requires us to create ONE builder and TWO functions for EACH button.
|
||||
* Create a builder, keep a weak reference to it to use it with all views on first inflation
|
||||
*/
|
||||
private static void bypassExpressionBuilder(){
|
||||
private static void buildExpressionBuilder(){
|
||||
ExpressionBuilder expressionBuilder = new ExpressionBuilder("1 + 1")
|
||||
.function(new Function("dp", 1) {
|
||||
@Override
|
||||
|
|
@ -245,11 +245,6 @@ public class ControlData {
|
|||
}
|
||||
});
|
||||
builder = new WeakReference<>(expressionBuilder);
|
||||
try {
|
||||
expression = new WeakReference<>(builder.get().getClass().getDeclaredField("expression"));
|
||||
expression.get().setAccessible(true);
|
||||
expression.get().set(expression.get(), expression.get().getModifiers() & ~Modifier.FINAL);
|
||||
}catch (Exception ignored){}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -257,10 +252,8 @@ public class ControlData {
|
|||
* @param stringExpression the expression to set.
|
||||
*/
|
||||
private static void setExpression(String stringExpression){
|
||||
if(builder.get() == null) bypassExpressionBuilder();
|
||||
try {
|
||||
expression.get().set(builder.get(), stringExpression);
|
||||
}catch (IllegalAccessException e){}
|
||||
if(builder.get() == null) buildExpressionBuilder();
|
||||
builder.get().expression(stringExpression);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -276,8 +269,8 @@ public class ControlData {
|
|||
keyValueMap.put("bottom", "DUMMY_BOTTOM");
|
||||
keyValueMap.put("width", "DUMMY_WIDTH");
|
||||
keyValueMap.put("height", "DUMMY_HEIGHT");
|
||||
keyValueMap.put("screen_width", Integer.toString(CallbackBridge.physicalWidth));
|
||||
keyValueMap.put("screen_height", Integer.toString(CallbackBridge.physicalHeight));
|
||||
keyValueMap.put("screen_width", "DUMMY_DATA" );
|
||||
keyValueMap.put("screen_height", "DUMMY_DATA");
|
||||
keyValueMap.put("margin", Integer.toString((int) Tools.dpToPx(2)));
|
||||
keyValueMap.put("preferred_scale", Float.toString(LauncherPreferences.PREF_BUTTONSIZE));
|
||||
|
||||
|
|
@ -300,6 +293,8 @@ public class ControlData {
|
|||
valueMap.put("bottom", Float.toString(CallbackBridge.physicalHeight - getHeight()));
|
||||
valueMap.put("width", Float.toString(getWidth()));
|
||||
valueMap.put("height", Float.toString(getHeight()));
|
||||
valueMap.put("screen_width",Integer.toString(CallbackBridge.physicalWidth));
|
||||
valueMap.put("screen_height",Integer.toString(CallbackBridge.physicalHeight));
|
||||
|
||||
return valueMap;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,14 +10,16 @@ import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.R
|
|||
import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.UP;
|
||||
import static net.kdt.pojavlaunch.customcontrols.ControlDrawerData.Orientation.FREE;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
@androidx.annotation.Keep
|
||||
public class ControlDrawerData {
|
||||
|
||||
public ArrayList<ControlData> buttonProperties;
|
||||
public ControlData properties;
|
||||
public Orientation orientation;
|
||||
|
||||
@androidx.annotation.Keep
|
||||
public enum Orientation {
|
||||
DOWN,
|
||||
LEFT,
|
||||
|
|
|
|||
|
|
@ -16,8 +16,7 @@ import net.kdt.pojavlaunch.customcontrols.buttons.ControlSubButton;
|
|||
import net.kdt.pojavlaunch.customcontrols.handleview.HandleView;
|
||||
import net.kdt.pojavlaunch.prefs.*;
|
||||
|
||||
public class ControlLayout extends FrameLayout
|
||||
{
|
||||
public class ControlLayout extends FrameLayout {
|
||||
protected CustomControls mLayout;
|
||||
private boolean mModifiable;
|
||||
private CustomControlsActivity mActivity;
|
||||
|
|
@ -252,6 +251,13 @@ public class ControlLayout extends FrameLayout
|
|||
return children;
|
||||
}
|
||||
|
||||
public void refreshControlButtonPositions(){
|
||||
for(ControlButton button : getButtonChildren()){
|
||||
button.setDynamicX(button.getProperties().dynamicX);
|
||||
button.setDynamicY(button.getProperties().dynamicY);
|
||||
}
|
||||
}
|
||||
|
||||
HashMap<View, ControlButton> mapTable = new HashMap<>();
|
||||
//While this is called onTouch, this should only be called from a ControlButton.
|
||||
public boolean onTouch(View v, MotionEvent ev) {
|
||||
|
|
|
|||
|
|
@ -1,12 +1,13 @@
|
|||
package net.kdt.pojavlaunch.customcontrols;
|
||||
import android.content.*;
|
||||
import com.google.gson.*;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.*;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
@Keep
|
||||
public class CustomControls {
|
||||
public int version = -1;
|
||||
public float scaledAt;
|
||||
|
|
@ -33,22 +34,22 @@ public class CustomControls {
|
|||
this.mControlDataList.add(new ControlData(ControlData.getSpecialButtons()[3])); // Secondary Mouse mControlDataList
|
||||
this.mControlDataList.add(new ControlData(ControlData.getSpecialButtons()[4])); // Virtual mouse toggle
|
||||
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_debug, new int[]{LWJGLGLFWKeycode.GLFW_KEY_F3}, "${margin}", "${margin}", false));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_chat, new int[]{LWJGLGLFWKeycode.GLFW_KEY_T}, "${margin} * 2 + ${width}", "${margin}", false));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_listplayers, new int[]{LWJGLGLFWKeycode.GLFW_KEY_TAB}, "${margin} * 4 + ${width} * 3", "${margin}", false));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_thirdperson, new int[]{LWJGLGLFWKeycode.GLFW_KEY_F5}, "${margin}", "${height} + ${margin}", false));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_debug, new int[]{LwjglGlfwKeycode.GLFW_KEY_F3}, "${margin}", "${margin}", false));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_chat, new int[]{LwjglGlfwKeycode.GLFW_KEY_T}, "${margin} * 2 + ${width}", "${margin}", false));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_listplayers, new int[]{LwjglGlfwKeycode.GLFW_KEY_TAB}, "${margin} * 4 + ${width} * 3", "${margin}", false));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_thirdperson, new int[]{LwjglGlfwKeycode.GLFW_KEY_F5}, "${margin}", "${height} + ${margin}", false));
|
||||
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_up, new int[]{LWJGLGLFWKeycode.GLFW_KEY_W}, "${margin} * 2 + ${width}", "${bottom} - ${margin} * 3 - ${height} * 2", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_left, new int[]{LWJGLGLFWKeycode.GLFW_KEY_A}, "${margin}", "${bottom} - ${margin} * 2 - ${height}", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_down, new int[]{LWJGLGLFWKeycode.GLFW_KEY_S}, "${margin} * 2 + ${width}", "${bottom} - ${margin}", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_right, new int[]{LWJGLGLFWKeycode.GLFW_KEY_D}, "${margin} * 3 + ${width} * 2", "${bottom} - ${margin} * 2 - ${height}", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_up, new int[]{LwjglGlfwKeycode.GLFW_KEY_W}, "${margin} * 2 + ${width}", "${bottom} - ${margin} * 3 - ${height} * 2", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_left, new int[]{LwjglGlfwKeycode.GLFW_KEY_A}, "${margin}", "${bottom} - ${margin} * 2 - ${height}", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_down, new int[]{LwjglGlfwKeycode.GLFW_KEY_S}, "${margin} * 2 + ${width}", "${bottom} - ${margin}", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_right, new int[]{LwjglGlfwKeycode.GLFW_KEY_D}, "${margin} * 3 + ${width} * 2", "${bottom} - ${margin} * 2 - ${height}", true));
|
||||
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_inventory, new int[]{LWJGLGLFWKeycode.GLFW_KEY_E}, "${margin} * 3 + ${width} * 2", "${bottom} - ${margin}", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_inventory, new int[]{LwjglGlfwKeycode.GLFW_KEY_E}, "${margin} * 3 + ${width} * 2", "${bottom} - ${margin}", true));
|
||||
|
||||
ControlData shiftData = new ControlData(ctx, R.string.control_shift, new int[]{LWJGLGLFWKeycode.GLFW_KEY_LEFT_SHIFT}, "${margin} * 2 + ${width}", "${screen_height} - ${margin} * 2 - ${height} * 2", true);
|
||||
ControlData shiftData = new ControlData(ctx, R.string.control_shift, new int[]{LwjglGlfwKeycode.GLFW_KEY_LEFT_SHIFT}, "${margin} * 2 + ${width}", "${screen_height} - ${margin} * 2 - ${height} * 2", true);
|
||||
shiftData.isToggle = true;
|
||||
this.mControlDataList.add(shiftData);
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_jump, new int[]{LWJGLGLFWKeycode.GLFW_KEY_SPACE}, "${right} - ${margin} * 2 - ${width}", "${bottom} - ${margin} * 2 - ${height}", true));
|
||||
this.mControlDataList.add(new ControlData(ctx, R.string.control_jump, new int[]{LwjglGlfwKeycode.GLFW_KEY_SPACE}, "${right} - ${margin} * 2 - ${width}", "${bottom} - ${margin} * 2 - ${height}", true));
|
||||
|
||||
//The default controls are conform to the V2
|
||||
version = 4;
|
||||
|
|
@ -56,7 +57,7 @@ public class CustomControls {
|
|||
|
||||
|
||||
public void save(String path) throws IOException {
|
||||
//Current version is the V2.4 so the version as to be marked as 4 !
|
||||
//Current version is the V2.5 so the version as to be marked as 4 !
|
||||
version = 4;
|
||||
|
||||
Tools.write(path, Tools.GLOBAL_GSON.toJson(this));
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package net.kdt.pojavlaunch.customcontrols;
|
|||
|
||||
import com.google.gson.JsonSyntaxException;
|
||||
|
||||
import net.kdt.pojavlaunch.LWJGLGLFWKeycode;
|
||||
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
||||
import org.json.JSONArray;
|
||||
|
|
@ -83,10 +83,10 @@ public class LayoutConverter {
|
|||
for(int i = 0; i < layoutMainArray.length(); i++) {
|
||||
JSONObject button = layoutMainArray.getJSONObject(i);
|
||||
ControlData n_button = new ControlData();
|
||||
int[] keycodes = new int[] {LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN,
|
||||
LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN,
|
||||
LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN,
|
||||
LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN};
|
||||
int[] keycodes = new int[] {LwjglGlfwKeycode.GLFW_KEY_UNKNOWN,
|
||||
LwjglGlfwKeycode.GLFW_KEY_UNKNOWN,
|
||||
LwjglGlfwKeycode.GLFW_KEY_UNKNOWN,
|
||||
LwjglGlfwKeycode.GLFW_KEY_UNKNOWN};
|
||||
n_button.isDynamicBtn = button.getBoolean("isDynamicBtn");
|
||||
n_button.dynamicX = button.getString("dynamicX");
|
||||
n_button.dynamicY = button.getString("dynamicY");
|
||||
|
|
@ -116,9 +116,9 @@ public class LayoutConverter {
|
|||
}
|
||||
if(button.getBoolean("isRound")) { n_button.cornerRadius = 35f; }
|
||||
int next_idx = 0;
|
||||
if(button.getBoolean("holdShift")) { keycodes[next_idx] = LWJGLGLFWKeycode.GLFW_KEY_LEFT_SHIFT; next_idx++; }
|
||||
if(button.getBoolean("holdCtrl")) { keycodes[next_idx] = LWJGLGLFWKeycode.GLFW_KEY_LEFT_CONTROL; next_idx++; }
|
||||
if(button.getBoolean("holdAlt")) { keycodes[next_idx] = LWJGLGLFWKeycode.GLFW_KEY_LEFT_ALT; next_idx++; }
|
||||
if(button.getBoolean("holdShift")) { keycodes[next_idx] = LwjglGlfwKeycode.GLFW_KEY_LEFT_SHIFT; next_idx++; }
|
||||
if(button.getBoolean("holdCtrl")) { keycodes[next_idx] = LwjglGlfwKeycode.GLFW_KEY_LEFT_CONTROL; next_idx++; }
|
||||
if(button.getBoolean("holdAlt")) { keycodes[next_idx] = LwjglGlfwKeycode.GLFW_KEY_LEFT_ALT; next_idx++; }
|
||||
keycodes[next_idx] = button.getInt("keycode");
|
||||
n_button.keycodes = keycodes;
|
||||
empty.mControlDataList.add(n_button);
|
||||
|
|
|
|||
|
|
@ -16,28 +16,28 @@ import net.kdt.pojavlaunch.customcontrols.ControlData;
|
|||
import net.kdt.pojavlaunch.customcontrols.ControlLayout;
|
||||
import net.kdt.pojavlaunch.customcontrols.handleview.*;
|
||||
import net.kdt.pojavlaunch.*;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
|
||||
import org.lwjgl.glfw.*;
|
||||
|
||||
import static net.kdt.pojavlaunch.BaseMainActivity.sendMouseButton;
|
||||
import static net.kdt.pojavlaunch.LWJGLGLFWKeycode.GLFW_KEY_UNKNOWN;
|
||||
import static net.kdt.pojavlaunch.LwjglGlfwKeycode.GLFW_KEY_UNKNOWN;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_BUTTONSIZE;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_BOTTOM_OFFSET;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_LEFT_OFFSET;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_RIGHT_OFFSET;
|
||||
import static net.kdt.pojavlaunch.prefs.LauncherPreferences.PREF_CONTROL_TOP_OFFSET;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendMouseButton;
|
||||
|
||||
@SuppressLint("ViewConstructor")
|
||||
public class ControlButton extends androidx.appcompat.widget.AppCompatButton implements OnLongClickListener
|
||||
{
|
||||
private final Paint mRectPaint = new Paint();;
|
||||
|
||||
public class ControlButton extends androidx.appcompat.widget.AppCompatButton implements OnLongClickListener {
|
||||
private final Paint mRectPaint = new Paint();
|
||||
protected GestureDetector mGestureDetector;
|
||||
protected ControlData mProperties;
|
||||
protected SelectionEndHandleView mHandleView;
|
||||
|
||||
protected boolean mModifiable = false;
|
||||
protected boolean mCanTriggerLongClick = true;
|
||||
|
||||
protected boolean isToggled = false;
|
||||
protected boolean isPointerOutOfBounds = false;
|
||||
protected boolean mIsToggled = false;
|
||||
protected boolean mIsPointerOutOfBounds = false;
|
||||
|
||||
public ControlButton(ControlLayout layout, ControlData properties) {
|
||||
super(layout.getContext());
|
||||
|
|
@ -152,16 +152,57 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
|
||||
@Override
|
||||
public void setX(float x) {
|
||||
// We have to account for control offset preference
|
||||
if(x + (mProperties.getWidth()/2f) > CallbackBridge.physicalWidth/2f){
|
||||
x -= PREF_CONTROL_RIGHT_OFFSET;
|
||||
}else{
|
||||
x += PREF_CONTROL_LEFT_OFFSET;
|
||||
}
|
||||
|
||||
super.setX(x);
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setY(float y) {
|
||||
// We have to account for control offset preference
|
||||
if(y - PREF_CONTROL_TOP_OFFSET + (mProperties.getHeight()/2f) > CallbackBridge.physicalHeight/2f){
|
||||
y -= PREF_CONTROL_BOTTOM_OFFSET;
|
||||
}else{
|
||||
y += PREF_CONTROL_TOP_OFFSET;
|
||||
}
|
||||
|
||||
super.setY(y);
|
||||
setModified(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getX() {
|
||||
float x = super.getX();
|
||||
// We have to account for control offset preference
|
||||
if(x + (mProperties.getWidth()/2f) > (CallbackBridge.physicalWidth)/2f){
|
||||
x += PREF_CONTROL_RIGHT_OFFSET;
|
||||
}else{
|
||||
x -= PREF_CONTROL_LEFT_OFFSET;
|
||||
}
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getY(){
|
||||
// We have to account for control offset preference
|
||||
float y = super.getY();
|
||||
if(y + (mProperties.getHeight()/2f) > CallbackBridge.physicalHeight/2f){
|
||||
y += PREF_CONTROL_BOTTOM_OFFSET;
|
||||
}else{
|
||||
y -= PREF_CONTROL_TOP_OFFSET;
|
||||
}
|
||||
|
||||
|
||||
return y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply the dynamic equation on the x axis.
|
||||
* @param dynamicX The equation to compute the position from
|
||||
|
|
@ -213,7 +254,7 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
super.onDraw(canvas);
|
||||
if (isToggled || (!mProperties.isToggle && isActivated()))
|
||||
if (mIsToggled || (!mProperties.isToggle && isActivated()))
|
||||
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), mProperties.cornerRadius, mProperties.cornerRadius, mRectPaint);
|
||||
}
|
||||
|
||||
|
|
@ -251,33 +292,33 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
case MotionEvent.ACTION_MOVE:
|
||||
//Send the event to be taken as a mouse action
|
||||
if(mProperties.passThruEnabled && CallbackBridge.isGrabbing()){
|
||||
MinecraftGLView v = ((ControlLayout) this.getParent()).findViewById(R.id.main_game_render_view);
|
||||
MinecraftGLSurface v = ((ControlLayout) this.getParent()).findViewById(R.id.main_game_render_view);
|
||||
if (v != null) v.dispatchTouchEvent(event);
|
||||
}
|
||||
|
||||
//If out of bounds
|
||||
if(event.getX() < getLeft() || event.getX() > getRight() ||
|
||||
event.getY() < getTop() || event.getY() > getBottom()){
|
||||
if(mProperties.isSwipeable && !isPointerOutOfBounds){
|
||||
if(mProperties.isSwipeable && !mIsPointerOutOfBounds){
|
||||
//Remove keys
|
||||
if(!triggerToggle()) {
|
||||
sendKeyPresses(false);
|
||||
}
|
||||
}
|
||||
isPointerOutOfBounds = true;
|
||||
mIsPointerOutOfBounds = true;
|
||||
((ControlLayout) getParent()).onTouch(this, event);
|
||||
break;
|
||||
}
|
||||
|
||||
//Else if we now are in bounds
|
||||
if(isPointerOutOfBounds) {
|
||||
if(mIsPointerOutOfBounds) {
|
||||
((ControlLayout) getParent()).onTouch(this, event);
|
||||
//RE-press the button
|
||||
if(mProperties.isSwipeable && !mProperties.isToggle){
|
||||
sendKeyPresses(true);
|
||||
}
|
||||
}
|
||||
isPointerOutOfBounds = false;
|
||||
mIsPointerOutOfBounds = false;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_DOWN: // 0
|
||||
|
|
@ -291,11 +332,11 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
case MotionEvent.ACTION_CANCEL: // 3
|
||||
case MotionEvent.ACTION_POINTER_UP: // 6
|
||||
if(mProperties.passThruEnabled){
|
||||
MinecraftGLView v = ((ControlLayout) this.getParent()).findViewById(R.id.main_game_render_view);
|
||||
MinecraftGLSurface v = ((ControlLayout) this.getParent()).findViewById(R.id.main_game_render_view);
|
||||
if (v != null) v.dispatchTouchEvent(event);
|
||||
}
|
||||
if(isPointerOutOfBounds) ((ControlLayout) getParent()).onTouch(this, event);
|
||||
isPointerOutOfBounds = false;
|
||||
if(mIsPointerOutOfBounds) ((ControlLayout) getParent()).onTouch(this, event);
|
||||
mIsPointerOutOfBounds = false;
|
||||
|
||||
if(!triggerToggle()) {
|
||||
sendKeyPresses(false);
|
||||
|
|
@ -350,7 +391,7 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
float MIN_DISTANCE = Tools.dpToPx(8);
|
||||
|
||||
if(button == this) return false;
|
||||
if(com.google.android.material.math.MathUtils.dist(
|
||||
if(net.kdt.pojavlaunch.utils.MathUtils.dist(
|
||||
button.getX() + button.getWidth()/2f,
|
||||
button.getY() + button.getHeight()/2f,
|
||||
getX() + getWidth()/2f,
|
||||
|
|
@ -455,9 +496,9 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
public boolean triggerToggle(){
|
||||
//returns true a the toggle system is triggered
|
||||
if(mProperties.isToggle){
|
||||
isToggled = !isToggled;
|
||||
mIsToggled = !mIsToggled;
|
||||
invalidate();
|
||||
sendKeyPresses(isToggled);
|
||||
sendKeyPresses(mIsToggled);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -467,7 +508,7 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
setActivated(isDown);
|
||||
for(int keycode : mProperties.keycodes){
|
||||
if(keycode >= GLFW_KEY_UNKNOWN){
|
||||
MainActivity.sendKeyPress(keycode, CallbackBridge.getCurrentMods(), isDown);
|
||||
sendKeyPress(keycode, CallbackBridge.getCurrentMods(), isDown);
|
||||
CallbackBridge.setModifiers(keycode, isDown);
|
||||
}else{
|
||||
sendSpecialKey(keycode, isDown);
|
||||
|
|
@ -490,21 +531,15 @@ public class ControlButton extends androidx.appcompat.widget.AppCompatButton imp
|
|||
break;
|
||||
|
||||
case ControlData.SPECIALBTN_MOUSEPRI:
|
||||
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT, isDown);
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT, isDown);
|
||||
break;
|
||||
|
||||
case ControlData.SPECIALBTN_MOUSEMID:
|
||||
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_MIDDLE, isDown);
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_MIDDLE, isDown);
|
||||
break;
|
||||
|
||||
case ControlData.SPECIALBTN_MOUSESEC:
|
||||
if (CallbackBridge.isGrabbing()) {
|
||||
sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown);
|
||||
} else {
|
||||
CallbackBridge.putMouseEventWithCoords(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown , CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
|
||||
BaseMainActivity.setRightOverride(isDown);
|
||||
}
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown);
|
||||
break;
|
||||
|
||||
case ControlData.SPECIALBTN_SCROLLDOWN:
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ package net.kdt.pojavlaunch.customcontrols.buttons;
|
|||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
|
|
@ -20,7 +19,7 @@ public class ControlDrawer extends ControlButton {
|
|||
|
||||
public ArrayList<ControlSubButton> buttons;
|
||||
public ControlDrawerData drawerData;
|
||||
public ControlLayout mLayout;
|
||||
public ControlLayout layout;
|
||||
public boolean areButtonsVisible;
|
||||
|
||||
|
||||
|
|
@ -28,14 +27,14 @@ public class ControlDrawer extends ControlButton {
|
|||
super(layout, drawerData.properties);
|
||||
|
||||
buttons = new ArrayList<>(drawerData.buttonProperties.size());
|
||||
mLayout = layout;
|
||||
this.layout = layout;
|
||||
this.drawerData = drawerData;
|
||||
areButtonsVisible = layout.getModifiable();
|
||||
}
|
||||
|
||||
|
||||
public void addButton(ControlData properties){
|
||||
addButton(new ControlSubButton(mLayout, properties, this));
|
||||
addButton(new ControlSubButton(layout, properties, this));
|
||||
}
|
||||
|
||||
public void addButton(ControlSubButton button){
|
||||
|
|
|
|||
|
|
@ -1,25 +1,28 @@
|
|||
package net.kdt.pojavlaunch.customcontrols.gamepad;
|
||||
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.content.Context;
|
||||
import android.view.Choreographer;
|
||||
import android.view.InputDevice;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.core.content.res.ResourcesCompat;
|
||||
import androidx.core.math.MathUtils;
|
||||
|
||||
import net.kdt.pojavlaunch.BaseMainActivity;
|
||||
import net.kdt.pojavlaunch.LWJGLGLFWKeycode;
|
||||
import net.kdt.pojavlaunch.MainActivity;
|
||||
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
||||
import net.kdt.pojavlaunch.utils.MCOptionUtils;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
import static net.kdt.pojavlaunch.Tools.currentDisplayMetrics;
|
||||
import static net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick.DIRECTION_EAST;
|
||||
import static net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick.DIRECTION_NONE;
|
||||
import static net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick.DIRECTION_NORTH;
|
||||
|
|
@ -30,40 +33,69 @@ import static net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick.DIRECTI
|
|||
import static net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick.DIRECTION_SOUTH_WEST;
|
||||
import static net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick.DIRECTION_WEST;
|
||||
import static net.kdt.pojavlaunch.customcontrols.gamepad.GamepadJoystick.isJoystickEvent;
|
||||
import static net.kdt.pojavlaunch.utils.MCOptionUtils.getMcScale;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendMouseButton;
|
||||
|
||||
public class Gamepad {
|
||||
|
||||
private final BaseMainActivity gameActivity;
|
||||
private final ImageView pointerView;
|
||||
/* Resolution scaler option, allow downsizing a window */
|
||||
private final float mScaleFactor = LauncherPreferences.DEFAULT_PREF.getInt("resolutionRatio",100)/100f;
|
||||
/* Mouse positions, scaled by the scaleFactor */
|
||||
private float mMouse_x, mMouse_y;
|
||||
/* Sensitivity, adjusted according to screen size */
|
||||
private final double mSensitivityFactor = (1.4 * (1080f/ currentDisplayMetrics.heightPixels));
|
||||
|
||||
private final GamepadDpad gamepadDpad = new GamepadDpad();
|
||||
private final ImageView mPointerImageView;
|
||||
|
||||
private final GamepadJoystick leftJoystick;
|
||||
private int currentJoystickDirection = DIRECTION_NONE;
|
||||
private final GamepadDpad mGamepadDpad = new GamepadDpad();
|
||||
|
||||
private final GamepadJoystick rightJoystick;
|
||||
private float lastHorizontalValue = 0.0f;
|
||||
private float lastVerticalValue = 0.0f;
|
||||
private final GamepadJoystick mLeftJoystick;
|
||||
private int mCurrentJoystickDirection = DIRECTION_NONE;
|
||||
|
||||
private final double mouseMaxAcceleration = 2f;
|
||||
private final GamepadJoystick mRightJoystick;
|
||||
private float mLastHorizontalValue = 0.0f;
|
||||
private float mLastVerticalValue = 0.0f;
|
||||
|
||||
private double mouseMagnitude;
|
||||
private double mouseAngle;
|
||||
private double mouseSensitivity = 19;
|
||||
private final double MOUSE_MAX_ACCELERATION = 2f;
|
||||
|
||||
private final GamepadMap gameMap = GamepadMap.getDefaultGameMap();
|
||||
private final GamepadMap menuMap = GamepadMap.getDefaultMenuMap();
|
||||
private GamepadMap currentMap = gameMap;
|
||||
private double mMouseMagnitude;
|
||||
private double mMouseAngle;
|
||||
private double mMouseSensitivity = 19;
|
||||
|
||||
private boolean lastGrabbingState = true;
|
||||
private final boolean mModifierDigitalTriggers;
|
||||
private final GamepadMap mGameMap = GamepadMap.getDefaultGameMap();
|
||||
private final GamepadMap mMenuMap = GamepadMap.getDefaultMenuMap();
|
||||
private GamepadMap mCurrentMap = mGameMap;
|
||||
|
||||
private boolean mLastGrabbingState = true;
|
||||
//private final boolean mModifierDigitalTriggers;
|
||||
private final boolean mModifierAnalogTriggers;
|
||||
private boolean mModifierSwappedAxis = true; //Triggers and right stick axis are swapped.
|
||||
|
||||
private final Handler inputHandler = new Handler(Looper.getMainLooper());
|
||||
private final Runnable switchStateRunnable;
|
||||
/* Choreographer with time to compute delta on ticking */
|
||||
private final Choreographer mScreenChoreographer;
|
||||
private long mLastFrameTime;
|
||||
|
||||
public Gamepad(BaseMainActivity gameActivity, InputDevice inputDevice){
|
||||
//Toast.makeText(gameActivity.getApplicationContext(),"GAMEPAD CREATED", Toast.LENGTH_LONG).show();
|
||||
/* Listen for change in gui scale */
|
||||
private MCOptionUtils.MCOptionListener mGuiScaleListener = () -> notifyGUISizeChange(getMcScale());
|
||||
|
||||
public Gamepad(View contextView, InputDevice inputDevice){
|
||||
mScreenChoreographer = Choreographer.getInstance();
|
||||
Choreographer.FrameCallback frameCallback = new Choreographer.FrameCallback() {
|
||||
@Override
|
||||
public void doFrame(long frameTimeNanos) {
|
||||
updateGrabbingState();
|
||||
tick(frameTimeNanos);
|
||||
mScreenChoreographer.postFrameCallback(this);
|
||||
}
|
||||
};
|
||||
mScreenChoreographer.postFrameCallback(frameCallback);
|
||||
mLastFrameTime = System.nanoTime();
|
||||
|
||||
/* Add the listener for the cross hair */
|
||||
MCOptionUtils.addMCOptionListener(mGuiScaleListener);
|
||||
|
||||
Toast.makeText(contextView.getContext(),"GAMEPAD CREATED", Toast.LENGTH_LONG).show();
|
||||
for(InputDevice.MotionRange range : inputDevice.getMotionRanges()){
|
||||
if(range.getAxis() == MotionEvent.AXIS_RTRIGGER
|
||||
|| range.getAxis() == MotionEvent.AXIS_LTRIGGER
|
||||
|
|
@ -72,96 +104,34 @@ public class Gamepad {
|
|||
mModifierSwappedAxis = false;
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
leftJoystick = new GamepadJoystick(MotionEvent.AXIS_X, MotionEvent.AXIS_Y, inputDevice);
|
||||
mLeftJoystick = new GamepadJoystick(MotionEvent.AXIS_X, MotionEvent.AXIS_Y, inputDevice);
|
||||
if(!mModifierSwappedAxis)
|
||||
rightJoystick = new GamepadJoystick(MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, inputDevice);
|
||||
mRightJoystick = new GamepadJoystick(MotionEvent.AXIS_Z, MotionEvent.AXIS_RZ, inputDevice);
|
||||
else
|
||||
rightJoystick = new GamepadJoystick(MotionEvent.AXIS_RX, MotionEvent.AXIS_RY, inputDevice);
|
||||
mRightJoystick = new GamepadJoystick(MotionEvent.AXIS_RX, MotionEvent.AXIS_RY, inputDevice);
|
||||
|
||||
mModifierDigitalTriggers = inputDevice.hasKeys(KeyEvent.KEYCODE_BUTTON_R2)[0];
|
||||
//mModifierDigitalTriggers = inputDevice.hasKeys(KeyEvent.KEYCODE_BUTTON_R2)[0];
|
||||
mModifierAnalogTriggers = supportAnalogTriggers(inputDevice);
|
||||
|
||||
this.gameActivity = gameActivity;
|
||||
pointerView = this.gameActivity.findViewById(R.id.console_pointer);
|
||||
pointerView.getDrawable().setFilterBitmap(false);
|
||||
notifyGUISizeChange(gameActivity.getMcScale());
|
||||
Context ctx = contextView.getContext();
|
||||
mPointerImageView = new ImageView(contextView.getContext());
|
||||
mPointerImageView.setImageDrawable(ResourcesCompat.getDrawable(ctx.getResources(), R.drawable.ic_gamepad_pointer, ctx.getTheme()));
|
||||
mPointerImageView.getDrawable().setFilterBitmap(false);
|
||||
|
||||
Runnable handlerRunnable = new Runnable() {
|
||||
int size = (int) ((22 * getMcScale()) / mScaleFactor);
|
||||
mPointerImageView.setLayoutParams(new FrameLayout.LayoutParams(size, size));
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
updateGrabbingState();
|
||||
tick();
|
||||
|
||||
inputHandler.postDelayed(this, 16);
|
||||
}
|
||||
};
|
||||
|
||||
inputHandler.postDelayed(handlerRunnable, 16);
|
||||
|
||||
//Initialize runnables to be used by the input system, avoiding generating one each time is better memory.
|
||||
switchStateRunnable = () -> {
|
||||
currentMap.resetPressedState();
|
||||
if(lastGrabbingState){
|
||||
currentMap = gameMap;
|
||||
pointerView.setVisibility(View.INVISIBLE);
|
||||
mouseSensitivity = 18;
|
||||
return;
|
||||
}
|
||||
|
||||
currentMap = menuMap;
|
||||
sendDirectionalKeycode(currentJoystickDirection, false, gameMap); // removing what we were doing
|
||||
|
||||
gameActivity.mouse_x = CallbackBridge.windowWidth/2;
|
||||
gameActivity.mouse_y = CallbackBridge.windowHeight/2;
|
||||
CallbackBridge.sendCursorPos(gameActivity.mouse_x, gameActivity.mouse_y);
|
||||
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
|
||||
pointerView.setVisibility(View.VISIBLE);
|
||||
//sensitivity in menu is MC and HARDWARE resolution dependent
|
||||
mouseSensitivity = 19 * gameActivity.scaleFactor / gameActivity.sensitivityFactor;
|
||||
};
|
||||
mMouse_x = CallbackBridge.windowWidth/2;
|
||||
mMouse_y = CallbackBridge.windowHeight/2;
|
||||
CallbackBridge.sendCursorPos(mMouse_x, mMouse_y);
|
||||
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
|
||||
|
||||
((ViewGroup)contextView.getParent()).addView(mPointerImageView);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private void tick(){
|
||||
//update mouse position
|
||||
if(lastHorizontalValue != 0 || lastVerticalValue != 0){
|
||||
GamepadJoystick currentJoystick = lastGrabbingState ? leftJoystick : rightJoystick;
|
||||
|
||||
double acceleration = (mouseMagnitude - currentJoystick.getDeadzone()) / (1 - currentJoystick.getDeadzone());
|
||||
acceleration = Math.pow(acceleration, mouseMaxAcceleration);
|
||||
if(acceleration > 1) acceleration = 1;
|
||||
|
||||
CallbackBridge.mouseX += Math.cos(mouseAngle) * acceleration * mouseSensitivity;
|
||||
CallbackBridge.mouseY -= Math.sin(mouseAngle) * acceleration * mouseSensitivity;
|
||||
|
||||
if(!lastGrabbingState){
|
||||
CallbackBridge.mouseX = MathUtils.clamp(CallbackBridge.mouseX, 0, CallbackBridge.windowWidth);
|
||||
CallbackBridge.mouseY = MathUtils.clamp(CallbackBridge.mouseY, 0, CallbackBridge.windowHeight);
|
||||
placePointerView((int) (CallbackBridge.mouseX /gameActivity.scaleFactor), (int) (CallbackBridge.mouseY/gameActivity.scaleFactor));
|
||||
}
|
||||
|
||||
gameActivity.mouse_x = CallbackBridge.mouseX;
|
||||
gameActivity.mouse_y = CallbackBridge.mouseY;
|
||||
|
||||
//Send the mouse to the game
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void updateGrabbingState() {
|
||||
boolean lastGrabbingValue = lastGrabbingState;
|
||||
lastGrabbingState = CallbackBridge.isGrabbing();
|
||||
if(lastGrabbingValue != lastGrabbingState){
|
||||
gameActivity.runOnUiThread(switchStateRunnable);
|
||||
}
|
||||
}
|
||||
|
||||
public void update(KeyEvent event){
|
||||
sendButton(event);
|
||||
}
|
||||
|
|
@ -170,32 +140,239 @@ public class Gamepad {
|
|||
updateDirectionalJoystick(event);
|
||||
updateMouseJoystick(event);
|
||||
updateAnalogTriggers(event);
|
||||
sendButton(gamepadDpad.convertEvent(event));
|
||||
|
||||
int[] dpadEvent = mGamepadDpad.convertEvent(event);
|
||||
sendButton(dpadEvent[0], dpadEvent[1]);
|
||||
}
|
||||
|
||||
private void updateMouseJoystick(MotionEvent event){
|
||||
GamepadJoystick currentJoystick = lastGrabbingState ? rightJoystick : leftJoystick;
|
||||
lastHorizontalValue = currentJoystick.getHorizontalAxis(event);
|
||||
lastVerticalValue = currentJoystick.getVerticalAxis(event);
|
||||
public void notifyGUISizeChange(int newSize){
|
||||
//Change the pointer size to match UI
|
||||
int size = (int) ((22 * newSize) / mScaleFactor);
|
||||
mPointerImageView.post(() -> mPointerImageView.setLayoutParams(new FrameLayout.LayoutParams(size, size)));
|
||||
|
||||
mouseMagnitude = currentJoystick.getMagnitude(event);
|
||||
mouseAngle = currentJoystick.getAngleRadian(event);
|
||||
}
|
||||
|
||||
private void updateDirectionalJoystick(MotionEvent event){
|
||||
GamepadJoystick currentJoystick = lastGrabbingState ? leftJoystick : rightJoystick;
|
||||
public void sendButton(KeyEvent event){
|
||||
sendButton(event.getKeyCode(), event.getAction());
|
||||
}
|
||||
|
||||
int lastJoystickDirection = currentJoystickDirection;
|
||||
currentJoystickDirection = currentJoystick.getHeightDirection(event);
|
||||
public void sendButton(int keycode, int action){
|
||||
boolean isDown = action == KeyEvent.ACTION_DOWN;
|
||||
switch (keycode){
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
getCurrentMap().BUTTON_A.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
getCurrentMap().BUTTON_B.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
getCurrentMap().BUTTON_X.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
getCurrentMap().BUTTON_Y.update(isDown);
|
||||
break;
|
||||
|
||||
if(currentJoystickDirection != lastJoystickDirection){
|
||||
sendDirectionalKeycode(lastJoystickDirection, false, getCurrentMap());
|
||||
sendDirectionalKeycode(currentJoystickDirection, true, getCurrentMap());
|
||||
//Shoulders
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
getCurrentMap().SHOULDER_LEFT.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
getCurrentMap().SHOULDER_RIGHT.update(isDown);
|
||||
break;
|
||||
|
||||
//Triggers
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
if(mModifierAnalogTriggers) break;
|
||||
getCurrentMap().TRIGGER_LEFT.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
if(mModifierAnalogTriggers) break;
|
||||
getCurrentMap().TRIGGER_RIGHT.update(isDown);
|
||||
break;
|
||||
|
||||
//L3 || R3
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
getCurrentMap().THUMBSTICK_LEFT.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
getCurrentMap().THUMBSTICK_RIGHT.update(isDown);
|
||||
break;
|
||||
|
||||
//DPAD
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
getCurrentMap().DPAD_UP.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
getCurrentMap().DPAD_DOWN.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
getCurrentMap().DPAD_LEFT.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
getCurrentMap().DPAD_RIGHT.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
getCurrentMap().DPAD_RIGHT.update(false);
|
||||
getCurrentMap().DPAD_LEFT.update(false);
|
||||
getCurrentMap().DPAD_UP.update(false);
|
||||
getCurrentMap().DPAD_DOWN.update(false);
|
||||
break;
|
||||
|
||||
//Start/select
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
getCurrentMap().BUTTON_START.update(isDown);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
getCurrentMap().BUTTON_SELECT.update(isDown);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_SPACE, CallbackBridge.getCurrentMods(), isDown);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendInput(int[] keycodes, boolean isDown){
|
||||
for(int keycode : keycodes){
|
||||
switch (keycode){
|
||||
case GamepadMap.MOUSE_SCROLL_DOWN:
|
||||
if(isDown) CallbackBridge.sendScroll(0, -1);
|
||||
break;
|
||||
case GamepadMap.MOUSE_SCROLL_UP:
|
||||
if(isDown) CallbackBridge.sendScroll(0, 1);
|
||||
break;
|
||||
|
||||
case LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT:
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown);
|
||||
break;
|
||||
case LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT:
|
||||
sendMouseButton(LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT, isDown);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
sendKeyPress(keycode, CallbackBridge.getCurrentMods(), isDown);
|
||||
break;
|
||||
}
|
||||
CallbackBridge.setModifiers(keycode, isDown);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean isGamepadEvent(MotionEvent event){
|
||||
return isJoystickEvent(event);
|
||||
}
|
||||
|
||||
public static boolean isGamepadEvent(KeyEvent event){
|
||||
return ((event.getDevice().getSources() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD
|
||||
&& GamepadDpad.isDpadEvent(event) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the new mouse position, computing the delta
|
||||
* @param frameTimeNanos The time to render the frame, used to compute mouse delta
|
||||
*/
|
||||
private void tick(long frameTimeNanos){
|
||||
//update mouse position
|
||||
if(mLastHorizontalValue != 0 || mLastVerticalValue != 0){
|
||||
GamepadJoystick currentJoystick = mLastGrabbingState ? mLeftJoystick : mRightJoystick;
|
||||
|
||||
double acceleration = (mMouseMagnitude - currentJoystick.getDeadzone()) / (1 - currentJoystick.getDeadzone());
|
||||
acceleration = Math.pow(acceleration, MOUSE_MAX_ACCELERATION);
|
||||
if(acceleration > 1) acceleration = 1;
|
||||
|
||||
// Compute delta since last tick time
|
||||
float deltaX = (float) (Math.cos(mMouseAngle) * acceleration * mMouseSensitivity);
|
||||
float deltaY = (float) (Math.sin(mMouseAngle) * acceleration * mMouseSensitivity);
|
||||
float deltaTimeScale = ((frameTimeNanos - mLastFrameTime) / 16666666f); // Scale of 1 = 60Hz
|
||||
deltaX *= deltaTimeScale;
|
||||
deltaY *= deltaTimeScale;
|
||||
|
||||
CallbackBridge.mouseX += deltaX;
|
||||
CallbackBridge.mouseY -= deltaY;
|
||||
|
||||
if(!mLastGrabbingState){
|
||||
CallbackBridge.mouseX = MathUtils.clamp(CallbackBridge.mouseX, 0, CallbackBridge.windowWidth);
|
||||
CallbackBridge.mouseY = MathUtils.clamp(CallbackBridge.mouseY, 0, CallbackBridge.windowHeight);
|
||||
placePointerView((int) (CallbackBridge.mouseX / mScaleFactor), (int) (CallbackBridge.mouseY/ mScaleFactor));
|
||||
}
|
||||
|
||||
mMouse_x = CallbackBridge.mouseX;
|
||||
mMouse_y = CallbackBridge.mouseY;
|
||||
|
||||
//Send the mouse to the game
|
||||
CallbackBridge.sendCursorPos(CallbackBridge.mouseX, CallbackBridge.mouseY);
|
||||
}
|
||||
|
||||
// Update last nano time
|
||||
mLastFrameTime = frameTimeNanos;
|
||||
}
|
||||
|
||||
/** Update the grabbing state, and change the currentMap, mouse position and sensibility */
|
||||
private void updateGrabbingState() {
|
||||
boolean lastGrabbingValue = mLastGrabbingState;
|
||||
mLastGrabbingState = CallbackBridge.isGrabbing();
|
||||
if(lastGrabbingValue == mLastGrabbingState) return;
|
||||
|
||||
// Switch grabbing state then
|
||||
mCurrentMap.resetPressedState();
|
||||
if(mLastGrabbingState){
|
||||
mCurrentMap = mGameMap;
|
||||
mPointerImageView.setVisibility(View.INVISIBLE);
|
||||
mMouseSensitivity = 18;
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentMap = mMenuMap;
|
||||
sendDirectionalKeycode(mCurrentJoystickDirection, false, mGameMap); // removing what we were doing
|
||||
|
||||
mMouse_x = CallbackBridge.windowWidth/2;
|
||||
mMouse_y = CallbackBridge.windowHeight/2;
|
||||
CallbackBridge.sendCursorPos(mMouse_x, mMouse_y);
|
||||
placePointerView(CallbackBridge.physicalWidth/2, CallbackBridge.physicalHeight/2);
|
||||
mPointerImageView.setVisibility(View.VISIBLE);
|
||||
// Sensitivity in menu is MC and HARDWARE resolution dependent
|
||||
mMouseSensitivity = 19 * mScaleFactor / mSensitivityFactor;
|
||||
|
||||
}
|
||||
|
||||
private void updateMouseJoystick(MotionEvent event){
|
||||
GamepadJoystick currentJoystick = mLastGrabbingState ? mRightJoystick : mLeftJoystick;
|
||||
float horizontalValue = currentJoystick.getHorizontalAxis(event);
|
||||
float verticalValue = currentJoystick.getVerticalAxis(event);
|
||||
if(horizontalValue != mLastHorizontalValue || verticalValue != mLastVerticalValue){
|
||||
mLastHorizontalValue = horizontalValue;
|
||||
mLastVerticalValue = verticalValue;
|
||||
|
||||
mMouseMagnitude = currentJoystick.getMagnitude(event);
|
||||
mMouseAngle = currentJoystick.getAngleRadian(event);
|
||||
|
||||
tick(System.nanoTime());
|
||||
return;
|
||||
}
|
||||
mLastHorizontalValue = horizontalValue;
|
||||
mLastVerticalValue = verticalValue;
|
||||
|
||||
mMouseMagnitude = currentJoystick.getMagnitude(event);
|
||||
mMouseAngle = currentJoystick.getAngleRadian(event);
|
||||
|
||||
}
|
||||
|
||||
private void updateDirectionalJoystick(MotionEvent event){
|
||||
GamepadJoystick currentJoystick = mLastGrabbingState ? mLeftJoystick : mRightJoystick;
|
||||
|
||||
int lastJoystickDirection = mCurrentJoystickDirection;
|
||||
mCurrentJoystickDirection = currentJoystick.getHeightDirection(event);
|
||||
|
||||
if(mCurrentJoystickDirection == lastJoystickDirection) return;
|
||||
|
||||
sendDirectionalKeycode(lastJoystickDirection, false, getCurrentMap());
|
||||
sendDirectionalKeycode(mCurrentJoystickDirection, true, getCurrentMap());
|
||||
}
|
||||
|
||||
private void updateAnalogTriggers(MotionEvent event){
|
||||
if(!mModifierDigitalTriggers){
|
||||
if(mModifierAnalogTriggers){
|
||||
getCurrentMap().TRIGGER_LEFT.update(
|
||||
(event.getAxisValue(MotionEvent.AXIS_LTRIGGER) > 0.5)
|
||||
|| (event.getAxisValue(MotionEvent.AXIS_BRAKE) > 0.5)
|
||||
|
|
@ -207,14 +384,27 @@ public class Gamepad {
|
|||
}
|
||||
}
|
||||
|
||||
public void notifyGUISizeChange(int newSize){
|
||||
//Change the pointer size to match UI
|
||||
int size = (int) ((22 * newSize) / gameActivity.scaleFactor);
|
||||
gameActivity.runOnUiThread(() -> pointerView.setLayoutParams(new FrameLayout.LayoutParams(size, size)));
|
||||
/**
|
||||
* Detect if a gamepad supports analog triggers
|
||||
* @param inputDevice The input device with all the MotionRange
|
||||
* @return Whether the gamepad supports analog triggers
|
||||
*/
|
||||
private boolean supportAnalogTriggers(InputDevice inputDevice){
|
||||
for(InputDevice.MotionRange motionRange : inputDevice.getMotionRanges()){
|
||||
int axis = motionRange.getAxis();
|
||||
|
||||
if( axis == MotionEvent.AXIS_BRAKE || axis == MotionEvent.AXIS_GAS ||
|
||||
axis == MotionEvent.AXIS_LTRIGGER || axis == MotionEvent.AXIS_RTRIGGER ||
|
||||
(mModifierSwappedAxis && axis == MotionEvent.AXIS_Z) ||
|
||||
(mModifierSwappedAxis && axis == MotionEvent.AXIS_RZ)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private GamepadMap getCurrentMap(){
|
||||
return currentMap;
|
||||
return mCurrentMap;
|
||||
}
|
||||
|
||||
private static void sendDirectionalKeycode(int direction, boolean isDown, GamepadMap map){
|
||||
|
|
@ -251,119 +441,7 @@ public class Gamepad {
|
|||
}
|
||||
|
||||
private void placePointerView(int x, int y){
|
||||
pointerView.setX(x - pointerView.getWidth()/2);
|
||||
pointerView.setY(y - pointerView.getHeight()/2);
|
||||
}
|
||||
|
||||
|
||||
public void sendButton(KeyEvent event){
|
||||
int keycode = event.getKeyCode();
|
||||
switch (keycode){
|
||||
case KeyEvent.KEYCODE_BUTTON_A:
|
||||
getCurrentMap().BUTTON_A.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_B:
|
||||
getCurrentMap().BUTTON_B.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_X:
|
||||
getCurrentMap().BUTTON_X.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_Y:
|
||||
getCurrentMap().BUTTON_Y.update(event);
|
||||
break;
|
||||
|
||||
//Shoulders
|
||||
case KeyEvent.KEYCODE_BUTTON_L1:
|
||||
getCurrentMap().SHOULDER_LEFT.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R1:
|
||||
getCurrentMap().SHOULDER_RIGHT.update(event);
|
||||
break;
|
||||
|
||||
//Triggers
|
||||
case KeyEvent.KEYCODE_BUTTON_L2:
|
||||
getCurrentMap().TRIGGER_LEFT.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_R2:
|
||||
getCurrentMap().TRIGGER_RIGHT.update(event);
|
||||
break;
|
||||
|
||||
//L3 || R3
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBL:
|
||||
getCurrentMap().THUMBSTICK_LEFT.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_THUMBR:
|
||||
getCurrentMap().THUMBSTICK_RIGHT.update(event);
|
||||
break;
|
||||
|
||||
//DPAD
|
||||
case KeyEvent.KEYCODE_DPAD_UP:
|
||||
getCurrentMap().DPAD_UP.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_DOWN:
|
||||
getCurrentMap().DPAD_DOWN.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_LEFT:
|
||||
getCurrentMap().DPAD_LEFT.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_RIGHT:
|
||||
getCurrentMap().DPAD_RIGHT.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_DPAD_CENTER:
|
||||
getCurrentMap().DPAD_RIGHT.update(false);
|
||||
getCurrentMap().DPAD_LEFT.update(false);
|
||||
getCurrentMap().DPAD_UP.update(false);
|
||||
getCurrentMap().DPAD_DOWN.update(false);
|
||||
break;
|
||||
|
||||
//Start/select
|
||||
case KeyEvent.KEYCODE_BUTTON_START:
|
||||
getCurrentMap().BUTTON_START.update(event);
|
||||
break;
|
||||
case KeyEvent.KEYCODE_BUTTON_SELECT:
|
||||
getCurrentMap().BUTTON_SELECT.update(event);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
MainActivity.sendKeyPress(LWJGLGLFWKeycode.GLFW_KEY_SPACE, CallbackBridge.getCurrentMods(), event.getAction() == KeyEvent.ACTION_DOWN);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public static void sendInput(int[] keycodes, boolean isDown){
|
||||
for(int keycode : keycodes){
|
||||
switch (keycode){
|
||||
case GamepadMap.MOUSE_SCROLL_DOWN:
|
||||
if(isDown) CallbackBridge.sendScroll(0, -1);
|
||||
break;
|
||||
case GamepadMap.MOUSE_SCROLL_UP:
|
||||
if(isDown) CallbackBridge.sendScroll(0, 1);
|
||||
break;
|
||||
|
||||
case LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT:
|
||||
MainActivity.sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT, isDown);
|
||||
break;
|
||||
case LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT:
|
||||
MainActivity.sendMouseButton(LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT, isDown);
|
||||
break;
|
||||
|
||||
|
||||
default:
|
||||
MainActivity.sendKeyPress(keycode, CallbackBridge.getCurrentMods(), isDown);
|
||||
break;
|
||||
}
|
||||
CallbackBridge.setModifiers(keycode, isDown);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static boolean isGamepadEvent(MotionEvent event){
|
||||
return isJoystickEvent(event);
|
||||
}
|
||||
|
||||
public static boolean isGamepadEvent(KeyEvent event){
|
||||
return ((event.getSource() & InputDevice.SOURCE_GAMEPAD) == InputDevice.SOURCE_GAMEPAD
|
||||
|| GamepadDpad.isDpadEvent(event) );
|
||||
mPointerImageView.setX(x - mPointerImageView.getWidth()/2);
|
||||
mPointerImageView.setY(y - mPointerImageView.getHeight()/2);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,15 +2,15 @@ package net.kdt.pojavlaunch.customcontrols.gamepad;
|
|||
|
||||
import android.view.KeyEvent;
|
||||
|
||||
/**
|
||||
* Simple button able to store its state and some properties
|
||||
*/
|
||||
public class GamepadButton {
|
||||
|
||||
/*
|
||||
Just a simple button, that auto deal with the great habit from android to just SPAAAMS input events
|
||||
*/
|
||||
public int[] keycodes;
|
||||
public boolean isToggleable = false;
|
||||
private boolean isDown = false;
|
||||
private boolean isToggled = false;
|
||||
private boolean mIsDown = false;
|
||||
private boolean mIsToggled = false;
|
||||
|
||||
public void update(KeyEvent event){
|
||||
boolean isKeyDown = (event.getAction() == KeyEvent.ACTION_DOWN);
|
||||
|
|
@ -18,29 +18,29 @@ public class GamepadButton {
|
|||
}
|
||||
|
||||
public void update(boolean isKeyDown){
|
||||
if(isKeyDown != isDown){
|
||||
isDown = isKeyDown;
|
||||
if(isKeyDown != mIsDown){
|
||||
mIsDown = isKeyDown;
|
||||
if(isToggleable){
|
||||
if(isKeyDown){
|
||||
isToggled = !isToggled;
|
||||
Gamepad.sendInput(keycodes, isToggled);
|
||||
mIsToggled = !mIsToggled;
|
||||
Gamepad.sendInput(keycodes, mIsToggled);
|
||||
}
|
||||
return;
|
||||
}
|
||||
Gamepad.sendInput(keycodes, isDown);
|
||||
Gamepad.sendInput(keycodes, mIsDown);
|
||||
}
|
||||
}
|
||||
|
||||
public void resetButtonState(){
|
||||
if(isDown || isToggled){
|
||||
if(mIsDown || mIsToggled){
|
||||
Gamepad.sendInput(keycodes, false);
|
||||
}
|
||||
isDown = false;
|
||||
isToggled = false;
|
||||
mIsDown = false;
|
||||
mIsToggled = false;
|
||||
}
|
||||
|
||||
public boolean isDown(){
|
||||
return isToggleable ? isToggled : isDown;
|
||||
return isToggleable ? mIsToggled : mIsDown;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,43 +4,25 @@ import android.view.InputDevice;
|
|||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import net.kdt.pojavlaunch.LWJGLGLFWKeycode;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
|
||||
import static android.view.InputDevice.KEYBOARD_TYPE_ALPHABETIC;
|
||||
import static android.view.InputDevice.KEYBOARD_TYPE_NON_ALPHABETIC;
|
||||
import static android.view.InputDevice.SOURCE_DPAD;
|
||||
import static android.view.InputDevice.SOURCE_GAMEPAD;
|
||||
import static android.view.KeyEvent.KEYCODE_DPAD_CENTER;
|
||||
import static android.view.KeyEvent.KEYCODE_DPAD_DOWN;
|
||||
import static android.view.KeyEvent.KEYCODE_DPAD_LEFT;
|
||||
import static android.view.KeyEvent.KEYCODE_DPAD_RIGHT;
|
||||
import static android.view.KeyEvent.KEYCODE_DPAD_UP;
|
||||
|
||||
/*
|
||||
Reflection is used to avoid memory churning, and only has an negative impact at start
|
||||
*/
|
||||
|
||||
public class GamepadDpad {
|
||||
private int mLastKeycode = KEYCODE_DPAD_CENTER;
|
||||
|
||||
|
||||
private int lastKeycode = KEYCODE_DPAD_CENTER;
|
||||
private KeyEvent dummyEvent = new KeyEvent(KeyEvent.ACTION_DOWN, lastKeycode);
|
||||
private Field eventCodeField;
|
||||
private Field eventActionField;
|
||||
|
||||
{
|
||||
try {
|
||||
eventCodeField = dummyEvent.getClass().getDeclaredField("mKeyCode");
|
||||
eventCodeField.setAccessible(true);
|
||||
|
||||
eventActionField = dummyEvent.getClass().getDeclaredField("mAction");
|
||||
eventActionField.setAccessible(true);
|
||||
} catch (NoSuchFieldException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public KeyEvent convertEvent(MotionEvent event){
|
||||
/**
|
||||
* Convert the event to a 2 int array: keycode and keyAction, similar to a keyEvent
|
||||
* @param event The motion to convert
|
||||
* @return int[0] keycode, int[1] keyAction
|
||||
*/
|
||||
public int[] convertEvent(MotionEvent event){
|
||||
// Use the hat axis value to find the D-pad direction
|
||||
float xaxis = event.getAxisValue(MotionEvent.AXIS_HAT_X);
|
||||
float yaxis = event.getAxisValue(MotionEvent.AXIS_HAT_Y);
|
||||
|
|
@ -49,44 +31,25 @@ public class GamepadDpad {
|
|||
// Check if the AXIS_HAT_X value is -1 or 1, and set the D-pad
|
||||
// LEFT and RIGHT direction accordingly.
|
||||
if (Float.compare(xaxis, -1.0f) == 0) {
|
||||
lastKeycode = KEYCODE_DPAD_LEFT;
|
||||
mLastKeycode = KEYCODE_DPAD_LEFT;
|
||||
} else if (Float.compare(xaxis, 1.0f) == 0) {
|
||||
lastKeycode = KEYCODE_DPAD_RIGHT;
|
||||
mLastKeycode = KEYCODE_DPAD_RIGHT;
|
||||
}
|
||||
// Check if the AXIS_HAT_Y value is -1 or 1, and set the D-pad
|
||||
// UP and DOWN direction accordingly.
|
||||
else if (Float.compare(yaxis, -1.0f) == 0) {
|
||||
lastKeycode = KEYCODE_DPAD_UP;
|
||||
mLastKeycode = KEYCODE_DPAD_UP;
|
||||
} else if (Float.compare(yaxis, 1.0f) == 0) {
|
||||
lastKeycode = KEYCODE_DPAD_DOWN;
|
||||
mLastKeycode = KEYCODE_DPAD_DOWN;
|
||||
}else {
|
||||
//No keycode change
|
||||
action = KeyEvent.ACTION_UP;
|
||||
}
|
||||
|
||||
setDummyEventKeycode(lastKeycode);
|
||||
setDummyEventAction(action);
|
||||
dummyEvent.setSource(SOURCE_DPAD);
|
||||
return dummyEvent;
|
||||
return new int[]{mLastKeycode, action};
|
||||
|
||||
}
|
||||
|
||||
private void setDummyEventKeycode(int fakeKeycode){
|
||||
try {
|
||||
eventCodeField.setInt(dummyEvent, fakeKeycode);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void setDummyEventAction(int action){
|
||||
try {
|
||||
eventActionField.setInt(dummyEvent, action);
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isDpadEvent(MotionEvent event) {
|
||||
// Check that input comes from a device with directional pads.
|
||||
// And... also the joystick since it declares sometimes as a joystick.
|
||||
|
|
@ -94,6 +57,7 @@ public class GamepadDpad {
|
|||
}
|
||||
|
||||
public static boolean isDpadEvent(KeyEvent event){
|
||||
return ((event.getSource() & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) && (event.getDevice().getKeyboardType() == KEYBOARD_TYPE_NON_ALPHABETIC);
|
||||
//return ((event.getSource() & InputDevice.SOURCE_DPAD) == InputDevice.SOURCE_DPAD) && (event.getDevice().getKeyboardType() == KEYBOARD_TYPE_NON_ALPHABETIC);
|
||||
return event.isFromSource(SOURCE_GAMEPAD) && event.getDevice().getKeyboardType() != KEYBOARD_TYPE_ALPHABETIC;
|
||||
}
|
||||
}
|
||||
|
|
@ -4,7 +4,7 @@ import android.view.InputDevice;
|
|||
import android.view.MotionEvent;
|
||||
|
||||
|
||||
import com.google.android.material.math.MathUtils;
|
||||
import net.kdt.pojavlaunch.utils.MathUtils;
|
||||
|
||||
public class GamepadJoystick {
|
||||
|
||||
|
|
@ -20,17 +20,14 @@ public class GamepadJoystick {
|
|||
public static final int DIRECTION_SOUTH = 6;
|
||||
public static final int DIRECTION_SOUTH_EAST = 7;
|
||||
|
||||
private final InputDevice device;
|
||||
|
||||
private final int verticalAxis;
|
||||
private final int horizontalAxis;
|
||||
private final InputDevice mInputDevice;
|
||||
private final int mVerticalAxis;
|
||||
private final int mHorizontalAxis;
|
||||
|
||||
public GamepadJoystick(int horizontalAxis, int verticalAxis, InputDevice device){
|
||||
this.verticalAxis = verticalAxis;
|
||||
this.horizontalAxis = horizontalAxis;
|
||||
this.device = device;
|
||||
|
||||
|
||||
this.mVerticalAxis = verticalAxis;
|
||||
this.mHorizontalAxis = horizontalAxis;
|
||||
this.mInputDevice = device;
|
||||
}
|
||||
|
||||
public double getAngleRadian(MotionEvent event){
|
||||
|
|
@ -48,29 +45,18 @@ public class GamepadJoystick {
|
|||
}
|
||||
|
||||
public double getMagnitude(MotionEvent event){
|
||||
float x = Math.abs(event.getAxisValue(horizontalAxis));
|
||||
float y = Math.abs(event.getAxisValue(verticalAxis));
|
||||
float x = Math.abs(event.getAxisValue(mHorizontalAxis));
|
||||
float y = Math.abs(event.getAxisValue(mVerticalAxis));
|
||||
|
||||
return MathUtils.dist(0,0, x, y);
|
||||
}
|
||||
|
||||
public float getVerticalAxis(MotionEvent event){
|
||||
return applyDeadzone(event, verticalAxis);
|
||||
return applyDeadzone(event, mVerticalAxis);
|
||||
}
|
||||
|
||||
public float getHorizontalAxis(MotionEvent event){
|
||||
return applyDeadzone(event, horizontalAxis);
|
||||
}
|
||||
|
||||
private float applyDeadzone(MotionEvent event, int axis){
|
||||
//This piece of code also modifies the value
|
||||
//to make it seem like there was no deadzone in the first place
|
||||
|
||||
double magnitude = getMagnitude(event);
|
||||
float deadzone = getDeadzone();
|
||||
if (magnitude < deadzone) return 0;
|
||||
|
||||
return (float) ( (event.getAxisValue(axis) / magnitude) * ((magnitude - deadzone) / (1 - deadzone)) );
|
||||
return applyDeadzone(event, mHorizontalAxis);
|
||||
}
|
||||
|
||||
public static boolean isJoystickEvent(MotionEvent event){
|
||||
|
|
@ -91,9 +77,20 @@ public class GamepadJoystick {
|
|||
*/
|
||||
public float getDeadzone() {
|
||||
try{
|
||||
return Math.max(device.getMotionRange(horizontalAxis).getFlat() * 1.9f, 0.2f);
|
||||
return Math.max(mInputDevice.getMotionRange(mHorizontalAxis).getFlat() * 1.9f, 0.2f);
|
||||
}catch (Exception e){
|
||||
return 0.2f;
|
||||
}
|
||||
}
|
||||
|
||||
private float applyDeadzone(MotionEvent event, int axis){
|
||||
//This piece of code also modifies the value
|
||||
//to make it seem like there was no deadzone in the first place
|
||||
|
||||
double magnitude = getMagnitude(event);
|
||||
float deadzone = getDeadzone();
|
||||
if (magnitude < deadzone) return 0;
|
||||
|
||||
return (float) ( (event.getAxisValue(axis) / magnitude) * ((magnitude - deadzone) / (1 - deadzone)) );
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package net.kdt.pojavlaunch.customcontrols.gamepad;
|
||||
|
||||
import net.kdt.pojavlaunch.LWJGLGLFWKeycode;
|
||||
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
|
||||
|
||||
public class GamepadMap {
|
||||
|
||||
|
|
@ -76,33 +76,33 @@ public class GamepadMap {
|
|||
public static GamepadMap getDefaultGameMap(){
|
||||
GamepadMap gameMap = new GamepadMap();
|
||||
|
||||
gameMap.BUTTON_A.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_SPACE};
|
||||
gameMap.BUTTON_B.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_Q};
|
||||
gameMap.BUTTON_X.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_E};
|
||||
gameMap.BUTTON_Y.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_F};
|
||||
gameMap.BUTTON_A.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_SPACE};
|
||||
gameMap.BUTTON_B.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_Q};
|
||||
gameMap.BUTTON_X.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_E};
|
||||
gameMap.BUTTON_Y.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_F};
|
||||
|
||||
gameMap.DIRECTION_FORWARD = new int[]{LWJGLGLFWKeycode.GLFW_KEY_W};
|
||||
gameMap.DIRECTION_BACKWARD = new int[]{LWJGLGLFWKeycode.GLFW_KEY_S};
|
||||
gameMap.DIRECTION_RIGHT = new int[]{LWJGLGLFWKeycode.GLFW_KEY_D};
|
||||
gameMap.DIRECTION_LEFT = new int[]{LWJGLGLFWKeycode.GLFW_KEY_A};
|
||||
gameMap.DIRECTION_FORWARD = new int[]{LwjglGlfwKeycode.GLFW_KEY_W};
|
||||
gameMap.DIRECTION_BACKWARD = new int[]{LwjglGlfwKeycode.GLFW_KEY_S};
|
||||
gameMap.DIRECTION_RIGHT = new int[]{LwjglGlfwKeycode.GLFW_KEY_D};
|
||||
gameMap.DIRECTION_LEFT = new int[]{LwjglGlfwKeycode.GLFW_KEY_A};
|
||||
|
||||
gameMap.DPAD_UP.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_LEFT_SHIFT};
|
||||
gameMap.DPAD_DOWN.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_O}; //For mods ?
|
||||
gameMap.DPAD_RIGHT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_K}; //For mods ?
|
||||
gameMap.DPAD_LEFT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_J}; //For mods ?
|
||||
gameMap.DPAD_UP.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_LEFT_SHIFT};
|
||||
gameMap.DPAD_DOWN.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_O}; //For mods ?
|
||||
gameMap.DPAD_RIGHT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_K}; //For mods ?
|
||||
gameMap.DPAD_LEFT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_J}; //For mods ?
|
||||
|
||||
gameMap.SHOULDER_LEFT.keycodes = new int[]{GamepadMap.MOUSE_SCROLL_UP};
|
||||
gameMap.SHOULDER_RIGHT.keycodes = new int[]{GamepadMap.MOUSE_SCROLL_DOWN};
|
||||
|
||||
gameMap.TRIGGER_LEFT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT};
|
||||
gameMap.TRIGGER_RIGHT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT};
|
||||
gameMap.TRIGGER_LEFT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT};
|
||||
gameMap.TRIGGER_RIGHT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT};
|
||||
|
||||
gameMap.THUMBSTICK_LEFT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_LEFT_CONTROL};
|
||||
gameMap.THUMBSTICK_RIGHT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_LEFT_SHIFT};
|
||||
gameMap.THUMBSTICK_LEFT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_LEFT_CONTROL};
|
||||
gameMap.THUMBSTICK_RIGHT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_LEFT_SHIFT};
|
||||
gameMap.THUMBSTICK_RIGHT.isToggleable = true;
|
||||
|
||||
gameMap.BUTTON_START.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_ESCAPE};
|
||||
gameMap.BUTTON_SELECT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_TAB};
|
||||
gameMap.BUTTON_START.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_ESCAPE};
|
||||
gameMap.BUTTON_SELECT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_TAB};
|
||||
|
||||
return gameMap;
|
||||
}
|
||||
|
|
@ -113,10 +113,10 @@ public class GamepadMap {
|
|||
public static GamepadMap getDefaultMenuMap(){
|
||||
GamepadMap menuMap = new GamepadMap();
|
||||
|
||||
menuMap.BUTTON_A.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_LEFT};
|
||||
menuMap.BUTTON_B.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_ESCAPE};
|
||||
menuMap.BUTTON_X.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT};
|
||||
menuMap.BUTTON_Y.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_LEFT_SHIFT, LWJGLGLFWKeycode.GLFW_MOUSE_BUTTON_RIGHT}; //Oops, doesn't work since left shift isn't properly applied.
|
||||
menuMap.BUTTON_A.keycodes = new int[]{LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_LEFT};
|
||||
menuMap.BUTTON_B.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_ESCAPE};
|
||||
menuMap.BUTTON_X.keycodes = new int[]{LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT};
|
||||
menuMap.BUTTON_Y.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_LEFT_SHIFT, LwjglGlfwKeycode.GLFW_MOUSE_BUTTON_RIGHT}; //Oops, doesn't work since left shift isn't properly applied.
|
||||
|
||||
menuMap.DIRECTION_FORWARD = new int[]{GamepadMap.MOUSE_SCROLL_UP, GamepadMap.MOUSE_SCROLL_UP, GamepadMap.MOUSE_SCROLL_UP, GamepadMap.MOUSE_SCROLL_UP, GamepadMap.MOUSE_SCROLL_UP};
|
||||
menuMap.DIRECTION_BACKWARD = new int[]{GamepadMap.MOUSE_SCROLL_DOWN, GamepadMap.MOUSE_SCROLL_DOWN, GamepadMap.MOUSE_SCROLL_DOWN, GamepadMap.MOUSE_SCROLL_DOWN, GamepadMap.MOUSE_SCROLL_DOWN};
|
||||
|
|
@ -124,9 +124,9 @@ public class GamepadMap {
|
|||
menuMap.DIRECTION_LEFT = new int[]{};
|
||||
|
||||
menuMap.DPAD_UP.keycodes = new int[]{};
|
||||
menuMap.DPAD_DOWN.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_O}; //For mods ?
|
||||
menuMap.DPAD_RIGHT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_K}; //For mods ?
|
||||
menuMap.DPAD_LEFT.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_J}; //For mods ?
|
||||
menuMap.DPAD_DOWN.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_O}; //For mods ?
|
||||
menuMap.DPAD_RIGHT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_K}; //For mods ?
|
||||
menuMap.DPAD_LEFT.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_J}; //For mods ?
|
||||
|
||||
menuMap.SHOULDER_LEFT.keycodes = new int[]{GamepadMap.MOUSE_SCROLL_UP};
|
||||
menuMap.SHOULDER_RIGHT.keycodes = new int[]{GamepadMap.MOUSE_SCROLL_DOWN};
|
||||
|
|
@ -137,7 +137,7 @@ public class GamepadMap {
|
|||
menuMap.THUMBSTICK_LEFT.keycodes = new int[]{};
|
||||
menuMap.THUMBSTICK_RIGHT.keycodes = new int[]{};
|
||||
|
||||
menuMap.BUTTON_START.keycodes = new int[]{LWJGLGLFWKeycode.GLFW_KEY_ESCAPE};
|
||||
menuMap.BUTTON_START.keycodes = new int[]{LwjglGlfwKeycode.GLFW_KEY_ESCAPE};
|
||||
menuMap.BUTTON_SELECT.keycodes = new int[]{};
|
||||
|
||||
return menuMap;
|
||||
|
|
|
|||
|
|
@ -35,19 +35,17 @@ import net.kdt.pojavlaunch.customcontrols.buttons.ControlSubButton;
|
|||
|
||||
import androidx.appcompat.app.*;
|
||||
|
||||
import com.rarepebble.colorpicker.ColorPickerView;
|
||||
|
||||
public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListener {
|
||||
|
||||
private TextView mEditTextView;
|
||||
private TextView mDeleteTextView;
|
||||
private TextView mCloneTextView;
|
||||
|
||||
private final ControlButton editedButton;
|
||||
private final ControlButton mEditedButton;
|
||||
|
||||
public ActionPopupWindow(HandleView handleView, ControlButton button){
|
||||
super(handleView);
|
||||
this.editedButton = button;
|
||||
this.mEditedButton = button;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
@ -97,18 +95,18 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe
|
|||
public void onClick(final View view) {
|
||||
if (view == mEditTextView) {
|
||||
|
||||
if(editedButton instanceof ControlSubButton){
|
||||
new EditControlSubButtonPopup((ControlSubButton) editedButton);
|
||||
if(mEditedButton instanceof ControlSubButton){
|
||||
new EditControlSubButtonPopup((ControlSubButton) mEditedButton);
|
||||
return;
|
||||
}
|
||||
|
||||
if(editedButton instanceof ControlDrawer){
|
||||
new EditControlDrawerPopup((ControlDrawer) editedButton);
|
||||
if(mEditedButton instanceof ControlDrawer){
|
||||
new EditControlDrawerPopup((ControlDrawer) mEditedButton);
|
||||
return;
|
||||
}
|
||||
|
||||
if(editedButton instanceof ControlButton){
|
||||
new EditControlButtonPopup((ControlButton) editedButton);
|
||||
if(mEditedButton instanceof ControlButton){
|
||||
new EditControlButtonPopup((ControlButton) mEditedButton);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -121,18 +119,18 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe
|
|||
alertBuilder.setPositiveButton(R.string.global_remove, (p1, p2) -> {
|
||||
ControlLayout layout = ((ControlLayout) mHandleView.mView.getParent());
|
||||
|
||||
if(editedButton instanceof ControlSubButton){
|
||||
layout.removeControlSubButton((ControlSubButton) editedButton);
|
||||
if(mEditedButton instanceof ControlSubButton){
|
||||
layout.removeControlSubButton((ControlSubButton) mEditedButton);
|
||||
return;
|
||||
}
|
||||
|
||||
if(editedButton instanceof ControlDrawer){
|
||||
layout.removeControlDrawer((ControlDrawer) editedButton);
|
||||
if(mEditedButton instanceof ControlDrawer){
|
||||
layout.removeControlDrawer((ControlDrawer) mEditedButton);
|
||||
return;
|
||||
}
|
||||
|
||||
if(editedButton instanceof ControlButton){
|
||||
layout.removeControlButton((ControlButton) editedButton);
|
||||
if(mEditedButton instanceof ControlButton){
|
||||
layout.removeControlButton((ControlButton) mEditedButton);
|
||||
}
|
||||
|
||||
layout.removeControlButton(mHandleView.mView);
|
||||
|
|
@ -140,18 +138,18 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe
|
|||
alertBuilder.setNegativeButton(android.R.string.cancel, null);
|
||||
alertBuilder.show();
|
||||
}else if(view == mCloneTextView) {
|
||||
if(editedButton instanceof ControlDrawer){
|
||||
ControlDrawerData cloneData = new ControlDrawerData(((ControlDrawer)editedButton).getDrawerData());
|
||||
if(mEditedButton instanceof ControlDrawer){
|
||||
ControlDrawerData cloneData = new ControlDrawerData(((ControlDrawer) mEditedButton).getDrawerData());
|
||||
cloneData.properties.dynamicX = "0.5 * ${screen_width}";
|
||||
cloneData.properties.dynamicY = "0.5 * ${screen_height}";
|
||||
((ControlLayout) mHandleView.mView.getParent()).addDrawer(cloneData);
|
||||
}else if(editedButton instanceof ControlSubButton){
|
||||
ControlData cloneData = new ControlData(editedButton.getProperties());
|
||||
}else if(mEditedButton instanceof ControlSubButton){
|
||||
ControlData cloneData = new ControlData(mEditedButton.getProperties());
|
||||
cloneData.dynamicX = "0.5 * ${screen_width}";
|
||||
cloneData.dynamicY = "0.5 * ${screen_height}";
|
||||
((ControlLayout) mHandleView.mView.getParent()).addSubButton(((ControlSubButton) editedButton).parentDrawer, cloneData);
|
||||
((ControlLayout) mHandleView.mView.getParent()).addSubButton(((ControlSubButton) mEditedButton).parentDrawer, cloneData);
|
||||
}else{
|
||||
ControlData cloneData = new ControlData(editedButton.getProperties());
|
||||
ControlData cloneData = new ControlData(mEditedButton.getProperties());
|
||||
cloneData.dynamicX = "0.5 * ${screen_width}";
|
||||
cloneData.dynamicY = "0.5 * ${screen_height}";
|
||||
((ControlLayout) mHandleView.mView.getParent()).addControlButton(cloneData);
|
||||
|
|
@ -177,23 +175,6 @@ public class ActionPopupWindow extends PinnedPopupWindow implements OnClickListe
|
|||
return positionY;
|
||||
}
|
||||
|
||||
|
||||
public static void showColorPicker(Context ctx,String title, boolean showAlpha, ImageView v){
|
||||
int startColor = ((ColorDrawable)v.getDrawable()).getColor();
|
||||
|
||||
ColorPickerView picker = new ColorPickerView(ctx);
|
||||
picker.setColor(startColor);
|
||||
picker.showAlpha(showAlpha);
|
||||
|
||||
AlertDialog.Builder dialog = new AlertDialog.Builder(ctx);
|
||||
dialog.setTitle(title);
|
||||
dialog.setView(picker);
|
||||
dialog.setNegativeButton(android.R.string.cancel, null);
|
||||
dialog.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> v.setImageDrawable(new ColorDrawable(picker.getColor())));
|
||||
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
public static void setPercentageText(TextView textView, int progress){
|
||||
textView.setText(progress + " %");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
package net.kdt.pojavlaunch.customcontrols.handleview;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
|
|
@ -8,9 +7,9 @@ import android.view.LayoutInflater;
|
|||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CompoundButton;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.SeekBar;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
|
@ -20,6 +19,7 @@ import androidx.appcompat.app.AlertDialog;
|
|||
import net.kdt.pojavlaunch.EfficientAndroidLWJGLKeycode;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.Tools;
|
||||
import net.kdt.pojavlaunch.colorselector.ColorSelector;
|
||||
import net.kdt.pojavlaunch.customcontrols.buttons.ControlButton;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlData;
|
||||
|
||||
|
|
@ -29,104 +29,106 @@ import static net.kdt.pojavlaunch.customcontrols.handleview.ActionPopupWindow.se
|
|||
|
||||
public class EditControlButtonPopup {
|
||||
|
||||
protected Dialog dialog;
|
||||
protected View v;
|
||||
protected AlertDialog.Builder builder;
|
||||
protected AlertDialog mDialog;
|
||||
protected View mRootView;
|
||||
protected AlertDialog.Builder mBuilder;
|
||||
|
||||
protected EditText editName;
|
||||
protected Spinner[] spinnersKeycode;
|
||||
protected EditText mNameEditText;
|
||||
protected Spinner[] mKeycodeSpinners;
|
||||
|
||||
protected CheckBox checkToggle;
|
||||
protected CheckBox checkPassThrough;
|
||||
protected CheckBox checkBoxSwipeable;
|
||||
protected CheckBox checkDynamicPosition;
|
||||
protected CheckBox mToggleCheckbox;
|
||||
protected CheckBox mPassthroughCheckbox;
|
||||
protected CheckBox mSwipeableCheckbox;
|
||||
protected CheckBox mDynamicPositionCheckbox;
|
||||
|
||||
protected EditText editWidth;
|
||||
protected EditText editHeight;
|
||||
protected EditText editDynamicX;
|
||||
protected EditText editDynamicY;
|
||||
protected EditText mWidthEditText;
|
||||
protected EditText mHeightEditText;
|
||||
protected EditText mDynamicXEditText;
|
||||
protected EditText mDynamicYEditText;
|
||||
|
||||
protected SeekBar seekBarOpacity;
|
||||
protected SeekBar seekBarCornerRadius;
|
||||
protected SeekBar seekBarStrokeWidth;
|
||||
protected SeekBar mOpacitySeekbar;
|
||||
protected SeekBar mCornerRadiusSeekbar;
|
||||
protected SeekBar mStrokeWidthSeekbar;
|
||||
|
||||
protected ImageButton buttonBackgroundColor;
|
||||
protected ImageButton buttonStrokeColor;
|
||||
protected ImageButton mBackgroundColorButton;
|
||||
protected ImageButton mStrokeColorButton;
|
||||
|
||||
protected TextView textOpacity;
|
||||
protected TextView textCornerRadius;
|
||||
protected TextView textStrokeWidth;
|
||||
protected TextView textStrokeColor;
|
||||
protected TextView mOpacityTextView;
|
||||
protected TextView mCornerRadiusTextView;
|
||||
protected TextView mStrokeWidthTextView;
|
||||
protected TextView mStrokeColorTextView;
|
||||
|
||||
protected final ControlButton button;
|
||||
protected final ControlData properties;
|
||||
protected ColorSelector mColorSelector;
|
||||
protected ImageView mEditingView;
|
||||
|
||||
protected ArrayAdapter<String> adapter;
|
||||
protected String[] specialArr;
|
||||
protected final ControlButton mControlButton;
|
||||
protected final ControlData mProperties;
|
||||
|
||||
protected ArrayAdapter<String> mAdapter;
|
||||
protected String[] mSpecialArray;
|
||||
|
||||
|
||||
public EditControlButtonPopup(ControlButton button){
|
||||
this.button = button;
|
||||
this.properties = button.getProperties();
|
||||
this.mControlButton = button;
|
||||
this.mProperties = button.getProperties();
|
||||
|
||||
initializeEditDialog(button.getContext());
|
||||
|
||||
//Create the finalized dialog
|
||||
dialog = builder.create();
|
||||
dialog.setOnShowListener(dialogInterface -> setEditDialogValues());
|
||||
mDialog = mBuilder.create();
|
||||
mDialog.setOnShowListener(dialogInterface -> setEditDialogValues());
|
||||
|
||||
|
||||
dialog.show();
|
||||
mDialog.show();
|
||||
}
|
||||
|
||||
protected void initializeEditDialog(Context ctx){
|
||||
//Create the editing dialog
|
||||
LayoutInflater layoutInflater = (LayoutInflater) ctx.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
v = layoutInflater.inflate(R.layout.control_button_setting,null);
|
||||
mRootView = layoutInflater.inflate(R.layout.dialog_control_button_setting,null);
|
||||
|
||||
builder = new AlertDialog.Builder(ctx);
|
||||
builder.setTitle(ctx.getResources().getString(R.string.customctrl_edit, properties.name));
|
||||
builder.setView(v);
|
||||
mBuilder = new AlertDialog.Builder(ctx);
|
||||
mBuilder.setTitle(ctx.getResources().getString(R.string.customctrl_edit, mProperties.name));
|
||||
mBuilder.setView(mRootView);
|
||||
|
||||
//Linking a lot of stuff
|
||||
editName = v.findViewById(R.id.editName_editText);
|
||||
mNameEditText = mRootView.findViewById(R.id.editName_editText);
|
||||
|
||||
spinnersKeycode = new Spinner[]{
|
||||
v.findViewById(R.id.editMapping_spinner_1),
|
||||
v.findViewById(R.id.editMapping_spinner_2),
|
||||
v.findViewById(R.id.editMapping_spinner_3),
|
||||
v.findViewById(R.id.editMapping_spinner_4)
|
||||
mKeycodeSpinners = new Spinner[]{
|
||||
mRootView.findViewById(R.id.editMapping_spinner_1),
|
||||
mRootView.findViewById(R.id.editMapping_spinner_2),
|
||||
mRootView.findViewById(R.id.editMapping_spinner_3),
|
||||
mRootView.findViewById(R.id.editMapping_spinner_4)
|
||||
};
|
||||
|
||||
checkToggle = v.findViewById(R.id.checkboxToggle);
|
||||
checkPassThrough = v.findViewById(R.id.checkboxPassThrough);
|
||||
checkBoxSwipeable = v.findViewById(R.id.checkboxSwipeable);
|
||||
mToggleCheckbox = mRootView.findViewById(R.id.checkboxToggle);
|
||||
mPassthroughCheckbox = mRootView.findViewById(R.id.checkboxPassThrough);
|
||||
mSwipeableCheckbox = mRootView.findViewById(R.id.checkboxSwipeable);
|
||||
|
||||
editWidth = v.findViewById(R.id.editSize_editTextX);
|
||||
editHeight = v.findViewById(R.id.editSize_editTextY);
|
||||
mWidthEditText = mRootView.findViewById(R.id.editSize_editTextX);
|
||||
mHeightEditText = mRootView.findViewById(R.id.editSize_editTextY);
|
||||
|
||||
editDynamicX = v.findViewById(R.id.editDynamicPositionX_editText);
|
||||
editDynamicY = v.findViewById(R.id.editDynamicPositionY_editText);
|
||||
mDynamicXEditText = mRootView.findViewById(R.id.editDynamicPositionX_editText);
|
||||
mDynamicYEditText = mRootView.findViewById(R.id.editDynamicPositionY_editText);
|
||||
|
||||
seekBarOpacity = v.findViewById(R.id.editButtonOpacity_seekbar);
|
||||
seekBarCornerRadius = v.findViewById(R.id.editCornerRadius_seekbar);
|
||||
seekBarStrokeWidth = v.findViewById(R.id.editStrokeWidth_seekbar);
|
||||
mOpacitySeekbar = mRootView.findViewById(R.id.editButtonOpacity_seekbar);
|
||||
mCornerRadiusSeekbar = mRootView.findViewById(R.id.editCornerRadius_seekbar);
|
||||
mStrokeWidthSeekbar = mRootView.findViewById(R.id.editStrokeWidth_seekbar);
|
||||
|
||||
SeekBar.OnSeekBarChangeListener changeListener = new SeekBar.OnSeekBarChangeListener() {
|
||||
|
||||
@Override
|
||||
public void onProgressChanged(SeekBar seekBar, int i, boolean b) {
|
||||
if(seekBar.equals(seekBarCornerRadius)) {
|
||||
setPercentageText(textCornerRadius, i);
|
||||
if(seekBar.equals(mCornerRadiusSeekbar)) {
|
||||
setPercentageText(mCornerRadiusTextView, i);
|
||||
return;
|
||||
}
|
||||
if(seekBar.equals(seekBarOpacity)) {
|
||||
setPercentageText(textOpacity, i);
|
||||
if(seekBar.equals(mOpacitySeekbar)) {
|
||||
setPercentageText(mOpacityTextView, i);
|
||||
return;
|
||||
}
|
||||
if(seekBar.equals(seekBarStrokeWidth)) {
|
||||
setPercentageText(textStrokeWidth, i);
|
||||
textStrokeColor.setVisibility(i == 0 ? View.GONE : View.VISIBLE);
|
||||
if(seekBar.equals(mStrokeWidthSeekbar)) {
|
||||
setPercentageText(mStrokeWidthTextView, i);
|
||||
mStrokeColorTextView.setVisibility(i == 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -137,43 +139,45 @@ public class EditControlButtonPopup {
|
|||
};
|
||||
|
||||
//Add listeners, too bad I don't need all the methods
|
||||
seekBarOpacity.setOnSeekBarChangeListener(changeListener);
|
||||
seekBarCornerRadius.setOnSeekBarChangeListener(changeListener);
|
||||
seekBarStrokeWidth.setOnSeekBarChangeListener(changeListener);
|
||||
mOpacitySeekbar.setOnSeekBarChangeListener(changeListener);
|
||||
mCornerRadiusSeekbar.setOnSeekBarChangeListener(changeListener);
|
||||
mStrokeWidthSeekbar.setOnSeekBarChangeListener(changeListener);
|
||||
|
||||
buttonBackgroundColor = v.findViewById(R.id.editBackgroundColor_imageButton);
|
||||
buttonStrokeColor = v.findViewById(R.id.editStrokeColor_imageButton);
|
||||
mBackgroundColorButton = mRootView.findViewById(R.id.editBackgroundColor_imageButton);
|
||||
mStrokeColorButton = mRootView.findViewById(R.id.editStrokeColor_imageButton);
|
||||
|
||||
textOpacity = v.findViewById(R.id.editButtonOpacity_textView_percent);
|
||||
textCornerRadius = v.findViewById(R.id.editCornerRadius_textView_percent);
|
||||
textStrokeWidth = v.findViewById(R.id.editStrokeWidth_textView_percent);
|
||||
textStrokeColor = v.findViewById(R.id.editStrokeColor_textView);
|
||||
mOpacityTextView = mRootView.findViewById(R.id.editButtonOpacity_textView_percent);
|
||||
mCornerRadiusTextView = mRootView.findViewById(R.id.editCornerRadius_textView_percent);
|
||||
mStrokeWidthTextView = mRootView.findViewById(R.id.editStrokeWidth_textView_percent);
|
||||
mStrokeColorTextView = mRootView.findViewById(R.id.editStrokeColor_textView);
|
||||
|
||||
checkDynamicPosition = v.findViewById(R.id.checkboxDynamicPosition);
|
||||
checkDynamicPosition.setOnCheckedChangeListener((btn, checked) -> {
|
||||
editDynamicX.setEnabled(checked);
|
||||
editDynamicY.setEnabled(checked);
|
||||
mDynamicPositionCheckbox = mRootView.findViewById(R.id.checkboxDynamicPosition);
|
||||
mDynamicPositionCheckbox.setOnCheckedChangeListener((btn, checked) -> {
|
||||
mDynamicXEditText.setEnabled(checked);
|
||||
mDynamicYEditText.setEnabled(checked);
|
||||
});
|
||||
|
||||
|
||||
//Initialize adapter for keycodes
|
||||
adapter = new ArrayAdapter<>(ctx, android.R.layout.simple_spinner_item);
|
||||
mAdapter = new ArrayAdapter<>(ctx, android.R.layout.simple_spinner_item);
|
||||
String[] oldSpecialArr = ControlData.buildSpecialButtonArray();
|
||||
specialArr = new String[oldSpecialArr.length];
|
||||
for (int i = 0; i < specialArr.length; i++) {
|
||||
specialArr[i] = "SPECIAL_" + oldSpecialArr[specialArr.length - i - 1];
|
||||
mSpecialArray = new String[oldSpecialArr.length];
|
||||
for (int i = 0; i < mSpecialArray.length; i++) {
|
||||
mSpecialArray[i] = "SPECIAL_" + oldSpecialArr[mSpecialArray.length - i - 1];
|
||||
}
|
||||
adapter.addAll(specialArr);
|
||||
adapter.addAll(EfficientAndroidLWJGLKeycode.generateKeyName());
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice);
|
||||
mAdapter.addAll(mSpecialArray);
|
||||
mAdapter.addAll(EfficientAndroidLWJGLKeycode.generateKeyName());
|
||||
mAdapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice);
|
||||
|
||||
for (Spinner spinner : spinnersKeycode) {
|
||||
spinner.setAdapter(adapter);
|
||||
for (Spinner spinner : mKeycodeSpinners) {
|
||||
spinner.setAdapter(mAdapter);
|
||||
}
|
||||
|
||||
mColorSelector = new ColorSelector(ctx,color -> mEditingView.setImageDrawable(new ColorDrawable(color)));
|
||||
|
||||
//Set color imageButton behavior
|
||||
buttonBackgroundColor.setOnClickListener(view -> ActionPopupWindow.showColorPicker(ctx, "Edit background color", true, buttonBackgroundColor));
|
||||
buttonStrokeColor.setOnClickListener(view -> ActionPopupWindow.showColorPicker(ctx, "Edit stroke color", false, buttonStrokeColor));
|
||||
mBackgroundColorButton.setOnClickListener(view -> showColorEditor((ImageView) view));
|
||||
mStrokeColorButton.setOnClickListener(view -> showColorEditor((ImageView) view));
|
||||
|
||||
|
||||
//Set dialog buttons behavior
|
||||
|
|
@ -186,36 +190,41 @@ public class EditControlButtonPopup {
|
|||
setupCheckerboards();
|
||||
}
|
||||
|
||||
protected void showColorEditor(ImageView imgView) {
|
||||
mEditingView = imgView;
|
||||
mColorSelector.show(((ColorDrawable)(imgView.getDrawable())).getColor());
|
||||
}
|
||||
|
||||
protected void setupDialogButtons(){
|
||||
//Set dialog buttons behavior
|
||||
builder.setPositiveButton(android.R.string.ok, (dialogInterface1, i) -> {
|
||||
if(!hasPropertiesErrors(dialog.getContext())){
|
||||
mBuilder.setPositiveButton(android.R.string.ok, (dialogInterface1, i) -> {
|
||||
if(!hasPropertiesErrors(mDialog.getContext())){
|
||||
saveProperties();
|
||||
}
|
||||
});
|
||||
builder.setNegativeButton(android.R.string.cancel, null);
|
||||
mBuilder.setNegativeButton(android.R.string.cancel, null);
|
||||
}
|
||||
|
||||
protected void hideUselessViews(){
|
||||
(v.findViewById(R.id.editOrientation_textView)).setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editOrientation_textView)).setVisibility(View.GONE);
|
||||
|
||||
(v.findViewById(R.id.editDynamicPositionX_textView)).setVisibility(View.GONE);
|
||||
(v.findViewById(R.id.editDynamicPositionY_textView)).setVisibility(View.GONE);
|
||||
editDynamicX.setVisibility(View.GONE);
|
||||
editDynamicY.setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editDynamicPositionX_textView)).setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editDynamicPositionY_textView)).setVisibility(View.GONE);
|
||||
mDynamicXEditText.setVisibility(View.GONE);
|
||||
mDynamicYEditText.setVisibility(View.GONE);
|
||||
|
||||
//Hide the color choice if the width is 0.
|
||||
textStrokeColor.setVisibility(properties.strokeWidth == 0 ? View.GONE : View.VISIBLE);
|
||||
mStrokeColorTextView.setVisibility(mProperties.strokeWidth == 0 ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
|
||||
protected void defineDynamicCheckChange(){
|
||||
checkDynamicPosition.setOnCheckedChangeListener((compoundButton, b) -> {
|
||||
mDynamicPositionCheckbox.setOnCheckedChangeListener((compoundButton, b) -> {
|
||||
int visibility = b ? View.VISIBLE : View.GONE;
|
||||
|
||||
(v.findViewById(R.id.editDynamicPositionX_textView)).setVisibility(visibility);
|
||||
(v.findViewById(R.id.editDynamicPositionY_textView)).setVisibility(visibility);
|
||||
editDynamicX.setVisibility(visibility);
|
||||
editDynamicY.setVisibility(visibility);
|
||||
(mRootView.findViewById(R.id.editDynamicPositionX_textView)).setVisibility(visibility);
|
||||
(mRootView.findViewById(R.id.editDynamicPositionY_textView)).setVisibility(visibility);
|
||||
mDynamicXEditText.setVisibility(visibility);
|
||||
mDynamicYEditText.setVisibility(visibility);
|
||||
});
|
||||
|
||||
}
|
||||
|
|
@ -227,65 +236,65 @@ public class EditControlButtonPopup {
|
|||
.size((int) Tools.dpToPx(20))
|
||||
.build();
|
||||
|
||||
buttonBackgroundColor.setBackground(drawable);
|
||||
buttonStrokeColor.setBackground(drawable);
|
||||
mBackgroundColorButton.setBackground(drawable);
|
||||
mStrokeColorButton.setBackground(drawable);
|
||||
}
|
||||
|
||||
protected void setEditDialogValues(){
|
||||
|
||||
editName.setText(properties.name);
|
||||
mNameEditText.setText(mProperties.name);
|
||||
|
||||
checkToggle.setChecked(properties.isToggle);
|
||||
checkPassThrough.setChecked(properties.passThruEnabled);
|
||||
checkBoxSwipeable.setChecked(properties.isSwipeable);
|
||||
mToggleCheckbox.setChecked(mProperties.isToggle);
|
||||
mPassthroughCheckbox.setChecked(mProperties.passThruEnabled);
|
||||
mSwipeableCheckbox.setChecked(mProperties.isSwipeable);
|
||||
|
||||
editWidth.setText(Float.toString(properties.getWidth()));
|
||||
editHeight.setText(Float.toString(properties.getHeight()));
|
||||
mWidthEditText.setText(Float.toString(mProperties.getWidth()));
|
||||
mHeightEditText.setText(Float.toString(mProperties.getHeight()));
|
||||
|
||||
editDynamicX.setEnabled(properties.isDynamicBtn);
|
||||
editDynamicY.setEnabled(properties.isDynamicBtn);
|
||||
editDynamicX.setText(properties.dynamicX);
|
||||
mDynamicXEditText.setEnabled(mProperties.isDynamicBtn);
|
||||
mDynamicYEditText.setEnabled(mProperties.isDynamicBtn);
|
||||
mDynamicXEditText.setText(mProperties.dynamicX);
|
||||
|
||||
editDynamicY.setText(properties.dynamicY);
|
||||
mDynamicYEditText.setText(mProperties.dynamicY);
|
||||
|
||||
seekBarOpacity.setProgress((int) (properties.opacity*100));
|
||||
seekBarStrokeWidth.setProgress(properties.strokeWidth);
|
||||
seekBarCornerRadius.setProgress((int)properties.cornerRadius);
|
||||
mOpacitySeekbar.setProgress((int) (mProperties.opacity*100));
|
||||
mStrokeWidthSeekbar.setProgress(mProperties.strokeWidth);
|
||||
mCornerRadiusSeekbar.setProgress((int) mProperties.cornerRadius);
|
||||
|
||||
buttonBackgroundColor.setImageDrawable(new ColorDrawable(properties.bgColor));
|
||||
buttonStrokeColor.setImageDrawable(new ColorDrawable(properties.strokeColor));
|
||||
mBackgroundColorButton.setImageDrawable(new ColorDrawable(mProperties.bgColor));
|
||||
mStrokeColorButton.setImageDrawable(new ColorDrawable(mProperties.strokeColor));
|
||||
|
||||
setPercentageText(textCornerRadius,seekBarCornerRadius.getProgress());
|
||||
setPercentageText(textOpacity,seekBarOpacity.getProgress());
|
||||
setPercentageText(textStrokeWidth,seekBarStrokeWidth.getProgress());
|
||||
setPercentageText(mCornerRadiusTextView, mCornerRadiusSeekbar.getProgress());
|
||||
setPercentageText(mOpacityTextView, mOpacitySeekbar.getProgress());
|
||||
setPercentageText(mStrokeWidthTextView, mStrokeWidthSeekbar.getProgress());
|
||||
|
||||
checkDynamicPosition.setChecked(properties.isDynamicBtn);
|
||||
mDynamicPositionCheckbox.setChecked(mProperties.isDynamicBtn);
|
||||
|
||||
for(int i=0; i< properties.keycodes.length; i++){
|
||||
if (properties.keycodes[i] < 0) {
|
||||
spinnersKeycode[i].setSelection(properties.keycodes[i] + specialArr.length);
|
||||
for(int i = 0; i< mProperties.keycodes.length; i++){
|
||||
if (mProperties.keycodes[i] < 0) {
|
||||
mKeycodeSpinners[i].setSelection(mProperties.keycodes[i] + mSpecialArray.length);
|
||||
} else {
|
||||
spinnersKeycode[i].setSelection(EfficientAndroidLWJGLKeycode.getIndexByValue(properties.keycodes[i]) + specialArr.length);
|
||||
mKeycodeSpinners[i].setSelection(EfficientAndroidLWJGLKeycode.getIndexByValue(mProperties.keycodes[i]) + mSpecialArray.length);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected boolean hasPropertiesErrors(Context ctx){
|
||||
if (editName.getText().toString().isEmpty()) {
|
||||
editName.setError(ctx.getResources().getString(R.string.global_error_field_empty));
|
||||
if (mNameEditText.getText().toString().isEmpty()) {
|
||||
mNameEditText.setError(ctx.getResources().getString(R.string.global_error_field_empty));
|
||||
return true;
|
||||
}
|
||||
|
||||
if (properties.isDynamicBtn) {
|
||||
if (mProperties.isDynamicBtn) {
|
||||
|
||||
int errorAt = 0;
|
||||
try {
|
||||
properties.insertDynamicPos(editDynamicX.getText().toString());
|
||||
mProperties.insertDynamicPos(mDynamicXEditText.getText().toString());
|
||||
errorAt = 1;
|
||||
properties.insertDynamicPos(editDynamicY.getText().toString());
|
||||
mProperties.insertDynamicPos(mDynamicYEditText.getText().toString());
|
||||
} catch (Throwable th) {
|
||||
(errorAt == 0 ? editDynamicX : editDynamicY).setError(th.getMessage());
|
||||
(errorAt == 0 ? mDynamicXEditText : mDynamicYEditText).setError(th.getMessage());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -296,36 +305,36 @@ public class EditControlButtonPopup {
|
|||
|
||||
protected void saveProperties(){
|
||||
//This method assumes there are no error.
|
||||
properties.name = editName.getText().toString();
|
||||
mProperties.name = mNameEditText.getText().toString();
|
||||
|
||||
//Keycodes
|
||||
for(int i=0; i<spinnersKeycode.length; ++i){
|
||||
if (spinnersKeycode[i].getSelectedItemPosition() < specialArr.length) {
|
||||
properties.keycodes[i] = spinnersKeycode[i].getSelectedItemPosition() - specialArr.length;
|
||||
for(int i = 0; i< mKeycodeSpinners.length; ++i){
|
||||
if (mKeycodeSpinners[i].getSelectedItemPosition() < mSpecialArray.length) {
|
||||
mProperties.keycodes[i] = mKeycodeSpinners[i].getSelectedItemPosition() - mSpecialArray.length;
|
||||
} else {
|
||||
properties.keycodes[i] = EfficientAndroidLWJGLKeycode.getValueByIndex(spinnersKeycode[i].getSelectedItemPosition() - specialArr.length);
|
||||
mProperties.keycodes[i] = EfficientAndroidLWJGLKeycode.getValueByIndex(mKeycodeSpinners[i].getSelectedItemPosition() - mSpecialArray.length);
|
||||
}
|
||||
}
|
||||
|
||||
properties.opacity = seekBarOpacity.getProgress()/100f;
|
||||
properties.strokeWidth = seekBarStrokeWidth.getProgress();
|
||||
properties.cornerRadius = seekBarCornerRadius.getProgress();
|
||||
mProperties.opacity = mOpacitySeekbar.getProgress()/100f;
|
||||
mProperties.strokeWidth = mStrokeWidthSeekbar.getProgress();
|
||||
mProperties.cornerRadius = mCornerRadiusSeekbar.getProgress();
|
||||
|
||||
properties.bgColor = ((ColorDrawable)buttonBackgroundColor.getDrawable()).getColor();
|
||||
properties.strokeColor = ((ColorDrawable) buttonStrokeColor.getDrawable()).getColor();
|
||||
mProperties.bgColor = ((ColorDrawable) mBackgroundColorButton.getDrawable()).getColor();
|
||||
mProperties.strokeColor = ((ColorDrawable) mStrokeColorButton.getDrawable()).getColor();
|
||||
|
||||
properties.isToggle = checkToggle.isChecked();
|
||||
properties.passThruEnabled = checkPassThrough.isChecked();
|
||||
properties.isSwipeable = checkBoxSwipeable.isChecked();
|
||||
mProperties.isToggle = mToggleCheckbox.isChecked();
|
||||
mProperties.passThruEnabled = mPassthroughCheckbox.isChecked();
|
||||
mProperties.isSwipeable = mSwipeableCheckbox.isChecked();
|
||||
|
||||
properties.setWidth(Float.parseFloat(editWidth.getText().toString()));
|
||||
properties.setHeight(Float.parseFloat(editHeight.getText().toString()));
|
||||
mProperties.setWidth(Float.parseFloat(mWidthEditText.getText().toString()));
|
||||
mProperties.setHeight(Float.parseFloat(mHeightEditText.getText().toString()));
|
||||
|
||||
properties.isDynamicBtn = checkDynamicPosition.isChecked();
|
||||
if(!editDynamicX.getText().toString().isEmpty()) properties.dynamicX = editDynamicX.getText().toString();
|
||||
if(!editDynamicY.getText().toString().isEmpty()) properties.dynamicY = editDynamicY.getText().toString();
|
||||
mProperties.isDynamicBtn = mDynamicPositionCheckbox.isChecked();
|
||||
if(!mDynamicXEditText.getText().toString().isEmpty()) mProperties.dynamicX = mDynamicXEditText.getText().toString();
|
||||
if(!mDynamicYEditText.getText().toString().isEmpty()) mProperties.dynamicY = mDynamicYEditText.getText().toString();
|
||||
|
||||
button.updateProperties();
|
||||
mControlButton.updateProperties();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import android.content.DialogInterface;
|
|||
import android.view.View;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.Toast;
|
||||
|
||||
import net.kdt.pojavlaunch.R;
|
||||
import net.kdt.pojavlaunch.customcontrols.ControlData;
|
||||
|
|
@ -13,63 +14,77 @@ import net.kdt.pojavlaunch.customcontrols.ControlDrawerData;
|
|||
import net.kdt.pojavlaunch.customcontrols.ControlLayout;
|
||||
|
||||
public class EditControlDrawerPopup extends EditControlButtonPopup{
|
||||
private Spinner spinnerOrientation;
|
||||
private Spinner mOrientationSpinner;
|
||||
|
||||
|
||||
private ControlDrawer drawer;
|
||||
private ControlDrawerData drawerData;
|
||||
private final ControlDrawer mDrawer;
|
||||
private final ControlDrawerData mDrawerData;
|
||||
|
||||
public EditControlDrawerPopup(ControlDrawer editedButton) {
|
||||
super(editedButton);
|
||||
drawer = editedButton;
|
||||
drawerData = editedButton.getDrawerData();
|
||||
mDrawer = editedButton;
|
||||
mDrawerData = editedButton.getDrawerData();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void hideUselessViews() {
|
||||
(v.findViewById(R.id.editMapping_textView)).setVisibility(View.GONE);
|
||||
checkPassThrough.setVisibility(View.GONE);
|
||||
checkToggle.setVisibility(View.GONE);
|
||||
checkBoxSwipeable.setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editMapping_textView)).setVisibility(View.GONE);
|
||||
mPassthroughCheckbox.setVisibility(View.GONE);
|
||||
mToggleCheckbox.setVisibility(View.GONE);
|
||||
mSwipeableCheckbox.setVisibility(View.GONE);
|
||||
|
||||
(mRootView.findViewById(R.id.editDynamicPositionX_textView)).setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editDynamicPositionY_textView)).setVisibility(View.GONE);
|
||||
mDynamicXEditText.setVisibility(View.GONE);
|
||||
mDynamicYEditText.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initializeEditDialog(Context ctx) {
|
||||
super.initializeEditDialog(ctx);
|
||||
|
||||
spinnerOrientation = v.findViewById(R.id.editOrientation_spinner);
|
||||
mOrientationSpinner = mRootView.findViewById(R.id.editOrientation_spinner);
|
||||
|
||||
ArrayAdapter<ControlDrawerData.Orientation> adapter = new ArrayAdapter<>(ctx, android.R.layout.simple_spinner_item);
|
||||
adapter.addAll(ControlDrawerData.getOrientations());
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice);
|
||||
|
||||
spinnerOrientation.setAdapter(adapter);
|
||||
mOrientationSpinner.setAdapter(adapter);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setEditDialogValues() {
|
||||
super.setEditDialogValues();
|
||||
|
||||
spinnerOrientation.setSelection(ControlDrawerData.orientationToInt(drawerData.orientation));
|
||||
mOrientationSpinner.setSelection(ControlDrawerData.orientationToInt(mDrawerData.orientation));
|
||||
|
||||
|
||||
//Using the dialog to replace the button behavior allows us not to dismiss the window
|
||||
mDialog.getButton(DialogInterface.BUTTON_NEUTRAL).setOnClickListener(v -> {
|
||||
ControlLayout layout = (ControlLayout) mDrawer.getParent();
|
||||
ControlData controlData = new ControlData(mDrawerData.properties);
|
||||
controlData.name = "new";
|
||||
layout.addSubButton(mDrawer, controlData);
|
||||
|
||||
Context ctx = mDialog.getContext();
|
||||
Toast.makeText(ctx, ctx.getString(R.string.customctrl_add_subbutton_message,
|
||||
mDrawer.getDrawerData().buttonProperties.size()), Toast.LENGTH_SHORT).show();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setupDialogButtons() {
|
||||
super.setupDialogButtons();
|
||||
|
||||
builder.setNeutralButton(v.getResources().getString(R.string.customctrl_addsubbutton), (dialogInterface, i) -> {
|
||||
ControlLayout layout = (ControlLayout) drawer.getParent();
|
||||
ControlData controlData = new ControlData(drawerData.properties);
|
||||
controlData.name = "new";
|
||||
layout.addSubButton(drawer, controlData);
|
||||
});
|
||||
mBuilder.setNeutralButton(mRootView.getResources().getString(R.string.customctrl_addsubbutton), null);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void saveProperties() {
|
||||
drawerData.orientation = ControlDrawerData.intToOrientation(spinnerOrientation.getSelectedItemPosition());
|
||||
mDrawerData.orientation = ControlDrawerData.intToOrientation(mOrientationSpinner.getSelectedItemPosition());
|
||||
super.saveProperties();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,15 +14,15 @@ public class EditControlSubButtonPopup extends EditControlButtonPopup{
|
|||
|
||||
@Override
|
||||
protected void hideUselessViews() {
|
||||
(v.findViewById(R.id.editSize_textView)).setVisibility(View.GONE);
|
||||
(v.findViewById(R.id.editOrientation_textView)).setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editSize_textView)).setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editOrientation_textView)).setVisibility(View.GONE);
|
||||
|
||||
checkDynamicPosition.setVisibility(View.GONE);
|
||||
mDynamicPositionCheckbox.setVisibility(View.GONE);
|
||||
|
||||
(v.findViewById(R.id.editDynamicPositionX_textView)).setVisibility(View.GONE);
|
||||
editDynamicX.setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editDynamicPositionX_textView)).setVisibility(View.GONE);
|
||||
mDynamicXEditText.setVisibility(View.GONE);
|
||||
|
||||
(v.findViewById(R.id.editDynamicPositionY_textView)).setVisibility(View.GONE);
|
||||
editDynamicY.setVisibility(View.GONE);
|
||||
(mRootView.findViewById(R.id.editDynamicPositionY_textView)).setVisibility(View.GONE);
|
||||
mDynamicYEditText.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,10 +29,8 @@ import net.kdt.pojavlaunch.*;
|
|||
import net.kdt.pojavlaunch.customcontrols.buttons.ControlButton;
|
||||
|
||||
|
||||
public abstract class HandleView extends View implements ViewPositionListener, View.OnLongClickListener
|
||||
{
|
||||
public abstract class HandleView extends View implements ViewPositionListener, View.OnLongClickListener {
|
||||
protected Drawable mDrawable;
|
||||
protected Drawable mDrawableLtr;
|
||||
protected Drawable mDrawableRtl;
|
||||
private final PopupWindow mContainer;
|
||||
// Position with respect to the parent TextView
|
||||
|
|
@ -65,12 +63,19 @@ public abstract class HandleView extends View implements ViewPositionListener, V
|
|||
// int mWindowPosX, mWindowPosY;
|
||||
|
||||
private PositionListener mPositionListener;
|
||||
public PositionListener getPositionListener() {
|
||||
if (mPositionListener == null) {
|
||||
mPositionListener = new PositionListener(mView);
|
||||
}
|
||||
return mPositionListener;
|
||||
}
|
||||
|
||||
// Touch-up filter: number of previous positions remembered
|
||||
private static final int HISTORY_SIZE = 5;
|
||||
private static final int TOUCH_UP_FILTER_DELAY_AFTER = 150;
|
||||
private static final int TOUCH_UP_FILTER_DELAY_BEFORE = 350;
|
||||
private final long[] mPreviousOffsetsTimes = new long[HISTORY_SIZE];
|
||||
private final int[] mPreviousOffsets = new int[HISTORY_SIZE];
|
||||
private int mPreviousOffsetIndex = 0;
|
||||
private int mNumberPreviousOffsets = 0;
|
||||
|
||||
// Addition
|
||||
private float mDownX, mDownY;
|
||||
|
||||
|
||||
public HandleView(ControlButton view) {
|
||||
super(view.getContext());
|
||||
|
|
@ -86,8 +91,7 @@ public abstract class HandleView extends View implements ViewPositionListener, V
|
|||
mContainer.setWindowLayoutType(WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL);
|
||||
mContainer.setContentView(this);
|
||||
|
||||
mDrawableLtr = view.getContext().getDrawable(R.drawable.text_select_handle_left_material);
|
||||
mDrawableRtl = view.getContext().getDrawable(R.drawable.text_select_handle_right_material);
|
||||
mDrawableRtl = view.getContext().getDrawable(R.drawable.ic_view_handle);
|
||||
mMinSize = view.getContext().getResources().getDimensionPixelSize(R.dimen.text_handle_min_size);
|
||||
|
||||
setOnLongClickListener(this);
|
||||
|
|
@ -99,11 +103,103 @@ public abstract class HandleView extends View implements ViewPositionListener, V
|
|||
mIdealVerticalOffset = 0.7f * handleHeight;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(getPreferredWidth(), getPreferredHeight());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas c) {
|
||||
final int drawWidth = mDrawable.getIntrinsicWidth();
|
||||
final int left = getHorizontalOffset();
|
||||
|
||||
mDrawable.setBounds(left, 0, left + drawWidth, mDrawable.getIntrinsicHeight());
|
||||
mDrawable.draw(c);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
ViewGroup.LayoutParams params = mView.getLayoutParams();
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
startTouchUpFilter(getCurrentCursorOffset());
|
||||
mTouchToWindowOffsetX = ev.getRawX() - mPositionX;
|
||||
mTouchToWindowOffsetY = ev.getRawY() - mPositionY;
|
||||
|
||||
final PositionListener positionListener = getPositionListener();
|
||||
mLastParentX = positionListener.getPositionX();
|
||||
mLastParentY = positionListener.getPositionY();
|
||||
mIsDragging = true;
|
||||
|
||||
// MOD: Addition
|
||||
mDownX = ev.getRawX();
|
||||
mDownY = ev.getRawY();
|
||||
mDownWidth = params.width;
|
||||
mDownHeight = params.height;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
final float rawX = ev.getRawX();
|
||||
final float rawY = ev.getRawY();
|
||||
|
||||
// Vertical hysteresis: vertical down movement tends to snap to ideal offset
|
||||
final float previousVerticalOffset = mTouchToWindowOffsetY - mLastParentY;
|
||||
final float currentVerticalOffset = rawY - mPositionY - mLastParentY;
|
||||
float newVerticalOffset;
|
||||
if (previousVerticalOffset < mIdealVerticalOffset) {
|
||||
newVerticalOffset = Math.min(currentVerticalOffset, mIdealVerticalOffset);
|
||||
newVerticalOffset = Math.max(newVerticalOffset, previousVerticalOffset);
|
||||
} else {
|
||||
newVerticalOffset = Math.max(currentVerticalOffset, mIdealVerticalOffset);
|
||||
newVerticalOffset = Math.min(newVerticalOffset, previousVerticalOffset);
|
||||
}
|
||||
mTouchToWindowOffsetY = newVerticalOffset + mLastParentY;
|
||||
|
||||
final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
|
||||
final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY;
|
||||
|
||||
int newWidth = (int) (mDownWidth + (rawX - mDownX));
|
||||
int newHeight = (int) (mDownHeight + (rawY - mDownY));
|
||||
|
||||
// mDownX = rawX;
|
||||
// mDownY = rawY;
|
||||
|
||||
params.width = Math.max(50, newWidth);
|
||||
params.height = Math.max(50, newHeight);
|
||||
|
||||
mView.setLayoutParams(params);
|
||||
|
||||
updatePosition(newPosX, newPosY);
|
||||
// break;
|
||||
return true;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
filterOnTouchUp();
|
||||
mIsDragging = false;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mIsDragging = false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public PositionListener getPositionListener() {
|
||||
if (mPositionListener == null) {
|
||||
mPositionListener = new PositionListener(mView);
|
||||
}
|
||||
return mPositionListener;
|
||||
}
|
||||
|
||||
protected void updateDrawable() {
|
||||
// final int offset = getCurrentCursorOffset();
|
||||
final boolean isRtlCharAtOffset = true; // mView.getLayout().isRtlCharAt(offset);
|
||||
mDrawable = isRtlCharAtOffset ? mDrawableRtl : mDrawableLtr;
|
||||
mDrawable = mDrawableRtl; //isRtlCharAtOffset ? mDrawableRtl : mDrawableLtr;
|
||||
mHotspotX = getHotspotX(mDrawable, isRtlCharAtOffset);
|
||||
mHorizontalGravity = getHorizontalGravity(isRtlCharAtOffset);
|
||||
}
|
||||
|
|
@ -111,14 +207,7 @@ public abstract class HandleView extends View implements ViewPositionListener, V
|
|||
protected abstract int getHotspotX(Drawable drawable, boolean isRtlRun);
|
||||
protected abstract int getHorizontalGravity(boolean isRtlRun);
|
||||
|
||||
// Touch-up filter: number of previous positions remembered
|
||||
private static final int HISTORY_SIZE = 5;
|
||||
private static final int TOUCH_UP_FILTER_DELAY_AFTER = 150;
|
||||
private static final int TOUCH_UP_FILTER_DELAY_BEFORE = 350;
|
||||
private final long[] mPreviousOffsetsTimes = new long[HISTORY_SIZE];
|
||||
private final int[] mPreviousOffsets = new int[HISTORY_SIZE];
|
||||
private int mPreviousOffsetIndex = 0;
|
||||
private int mNumberPreviousOffsets = 0;
|
||||
|
||||
|
||||
private void startTouchUpFilter(int offset) {
|
||||
mNumberPreviousOffsets = 0;
|
||||
|
|
@ -152,11 +241,6 @@ public abstract class HandleView extends View implements ViewPositionListener, V
|
|||
return mNumberPreviousOffsets > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
setMeasuredDimension(getPreferredWidth(), getPreferredHeight());
|
||||
}
|
||||
|
||||
private int getPreferredWidth() {
|
||||
return Math.max(mDrawable.getIntrinsicWidth(), mMinSize);
|
||||
}
|
||||
|
|
@ -283,15 +367,6 @@ public abstract class HandleView extends View implements ViewPositionListener, V
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas c) {
|
||||
final int drawWidth = mDrawable.getIntrinsicWidth();
|
||||
final int left = getHorizontalOffset();
|
||||
|
||||
mDrawable.setBounds(left, 0, left + drawWidth, mDrawable.getIntrinsicHeight());
|
||||
mDrawable.draw(c);
|
||||
}
|
||||
|
||||
private int getHorizontalOffset() {
|
||||
final int width = getPreferredWidth();
|
||||
final int drawWidth = mDrawable.getIntrinsicWidth();
|
||||
|
|
@ -315,81 +390,6 @@ public abstract class HandleView extends View implements ViewPositionListener, V
|
|||
return 0;
|
||||
}
|
||||
|
||||
// Addition
|
||||
private float mDownX, mDownY;
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
ViewGroup.LayoutParams params = mView.getLayoutParams();
|
||||
|
||||
switch (ev.getActionMasked()) {
|
||||
case MotionEvent.ACTION_DOWN: {
|
||||
startTouchUpFilter(getCurrentCursorOffset());
|
||||
mTouchToWindowOffsetX = ev.getRawX() - mPositionX;
|
||||
mTouchToWindowOffsetY = ev.getRawY() - mPositionY;
|
||||
|
||||
final PositionListener positionListener = getPositionListener();
|
||||
mLastParentX = positionListener.getPositionX();
|
||||
mLastParentY = positionListener.getPositionY();
|
||||
mIsDragging = true;
|
||||
|
||||
// MOD: Addition
|
||||
mDownX = ev.getRawX();
|
||||
mDownY = ev.getRawY();
|
||||
mDownWidth = params.width;
|
||||
mDownHeight = params.height;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
final float rawX = ev.getRawX();
|
||||
final float rawY = ev.getRawY();
|
||||
|
||||
// Vertical hysteresis: vertical down movement tends to snap to ideal offset
|
||||
final float previousVerticalOffset = mTouchToWindowOffsetY - mLastParentY;
|
||||
final float currentVerticalOffset = rawY - mPositionY - mLastParentY;
|
||||
float newVerticalOffset;
|
||||
if (previousVerticalOffset < mIdealVerticalOffset) {
|
||||
newVerticalOffset = Math.min(currentVerticalOffset, mIdealVerticalOffset);
|
||||
newVerticalOffset = Math.max(newVerticalOffset, previousVerticalOffset);
|
||||
} else {
|
||||
newVerticalOffset = Math.max(currentVerticalOffset, mIdealVerticalOffset);
|
||||
newVerticalOffset = Math.min(newVerticalOffset, previousVerticalOffset);
|
||||
}
|
||||
mTouchToWindowOffsetY = newVerticalOffset + mLastParentY;
|
||||
|
||||
final float newPosX = rawX - mTouchToWindowOffsetX + mHotspotX;
|
||||
final float newPosY = rawY - mTouchToWindowOffsetY + mTouchOffsetY;
|
||||
|
||||
int newWidth = (int) (mDownWidth + (rawX - mDownX));
|
||||
int newHeight = (int) (mDownHeight + (rawY - mDownY));
|
||||
|
||||
// mDownX = rawX;
|
||||
// mDownY = rawY;
|
||||
|
||||
params.width = Math.max(50, newWidth);
|
||||
params.height = Math.max(50, newHeight);
|
||||
|
||||
mView.setLayoutParams(params);
|
||||
|
||||
updatePosition(newPosX, newPosY);
|
||||
// break;
|
||||
return true;
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_UP:
|
||||
filterOnTouchUp();
|
||||
mIsDragging = false;
|
||||
break;
|
||||
|
||||
case MotionEvent.ACTION_CANCEL:
|
||||
mIsDragging = false;
|
||||
break;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean isDragging() {
|
||||
return mIsDragging;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,7 @@ import android.view.*;
|
|||
import net.kdt.pojavlaunch.customcontrols.buttons.ControlButton;
|
||||
|
||||
|
||||
public class SelectionEndHandleView extends HandleView
|
||||
{
|
||||
public class SelectionEndHandleView extends HandleView {
|
||||
public SelectionEndHandleView(ControlButton view) {
|
||||
super(view);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,5 +20,5 @@
|
|||
package net.kdt.pojavlaunch.customcontrols.handleview;
|
||||
|
||||
public interface ViewPositionListener {
|
||||
public void updatePosition(int parentPositionX, int parentPositionY, boolean parentPositionChanged, boolean parentScrolled);
|
||||
void updatePosition(int parentPositionX, int parentPositionY, boolean parentPositionChanged, boolean parentScrolled);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
package net.kdt.pojavlaunch.customcontrols.keyboard;
|
||||
|
||||
import net.kdt.pojavlaunch.AWTInputBridge;
|
||||
import net.kdt.pojavlaunch.AWTInputEvent;
|
||||
|
||||
/** Send chars via the AWT Bridgee */
|
||||
public class AwtCharSender implements CharacterSenderStrategy {
|
||||
@Override
|
||||
public void sendBackspace() {
|
||||
AWTInputBridge.sendKey(' ', AWTInputEvent.VK_BACK_SPACE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEnter() {
|
||||
AWTInputBridge.sendKey(' ', AWTInputEvent.VK_ENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChar(char character) {
|
||||
AWTInputBridge.sendChar(character);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
package net.kdt.pojavlaunch.customcontrols.keyboard;
|
||||
|
||||
/** Simple interface for sending chars through whatever bridge will be necessary */
|
||||
public interface CharacterSenderStrategy {
|
||||
/** Called when there is a character to delete, may be called multiple times in a row */
|
||||
void sendBackspace();
|
||||
|
||||
/** Called when we want to send enter specifically */
|
||||
void sendEnter();
|
||||
|
||||
/** Called when there is a character to send, may be called multiple times in a row */
|
||||
void sendChar(char character);
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package net.kdt.pojavlaunch.customcontrols.keyboard;
|
||||
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
|
||||
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
||||
/** Sends keys via the CallBackBridge */
|
||||
public class LwjglCharSender implements CharacterSenderStrategy {
|
||||
@Override
|
||||
public void sendBackspace() {
|
||||
CallbackBridge.sendKeycode(LwjglGlfwKeycode.GLFW_KEY_BACKSPACE, '\u0008', 0, 0, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendEnter() {
|
||||
sendKeyPress(LwjglGlfwKeycode.GLFW_KEY_ENTER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendChar(char character) {
|
||||
CallbackBridge.sendChar(character, 0);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,21 +1,21 @@
|
|||
package net.kdt.pojavlaunch.customcontrols;
|
||||
package net.kdt.pojavlaunch.customcontrols.keyboard;
|
||||
|
||||
|
||||
import static android.content.Context.INPUT_METHOD_SERVICE;
|
||||
|
||||
import static org.lwjgl.glfw.CallbackBridge.sendKeyPress;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.res.Configuration;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import net.kdt.pojavlaunch.BaseMainActivity;
|
||||
import net.kdt.pojavlaunch.LWJGLGLFWKeycode;
|
||||
import net.kdt.pojavlaunch.LwjglGlfwKeycode;
|
||||
import net.kdt.pojavlaunch.R;
|
||||
|
||||
import org.lwjgl.glfw.CallbackBridge;
|
||||
|
|
@ -36,7 +36,8 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
}
|
||||
|
||||
|
||||
private boolean isDoingInternalChanges = false;
|
||||
private boolean mIsDoingInternalChanges = false;
|
||||
private CharacterSenderStrategy mCharacterSender;
|
||||
|
||||
/**
|
||||
* We take the new chars, and send them to the game.
|
||||
|
|
@ -46,14 +47,16 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
@Override
|
||||
protected void onTextChanged(CharSequence text, int start, int lengthBefore, int lengthAfter) {
|
||||
super.onTextChanged(text, start, lengthBefore, lengthAfter);
|
||||
if(isDoingInternalChanges)return;
|
||||
if(mIsDoingInternalChanges)return;
|
||||
if(mCharacterSender != null){
|
||||
for(int i=0; i < lengthBefore; ++i){
|
||||
mCharacterSender.sendBackspace();
|
||||
}
|
||||
|
||||
for(int i=0; i< lengthBefore; ++i){
|
||||
CallbackBridge.sendKeycode(LWJGLGLFWKeycode.GLFW_KEY_BACKSPACE, '\u0008', 0, 0, true);
|
||||
}
|
||||
for(int i=start, count = 0; count < lengthAfter; ++i){
|
||||
CallbackBridge.sendChar(text.charAt(i), 0);
|
||||
++count;
|
||||
for(int i=start, count = 0; count < lengthAfter; ++i){
|
||||
mCharacterSender.sendChar(text.charAt(i));
|
||||
++count;
|
||||
}
|
||||
}
|
||||
|
||||
//Reset the keyboard state
|
||||
|
|
@ -113,25 +116,15 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
*/
|
||||
@SuppressLint("SetTextI18n")
|
||||
public void clear(){
|
||||
isDoingInternalChanges = true;
|
||||
mIsDoingInternalChanges = true;
|
||||
//Braille space, doesn't trigger keyboard auto-complete
|
||||
//replacing directly the text without though setText avoids notifying changes
|
||||
setText(" ");
|
||||
setSelection(getText().length());
|
||||
isDoingInternalChanges = false;
|
||||
mIsDoingInternalChanges = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Send the enter key.
|
||||
*/
|
||||
private void sendEnter(){
|
||||
BaseMainActivity.sendKeyPress(LWJGLGLFWKeycode.GLFW_KEY_ENTER);
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Regain ability to exist, take focus and have some text being input
|
||||
*/
|
||||
/** Regain ability to exist, take focus and have some text being input */
|
||||
public void enable(){
|
||||
setEnabled(true);
|
||||
setFocusable(true);
|
||||
|
|
@ -140,9 +133,7 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Lose ability to exist, take focus and have some text being input
|
||||
*/
|
||||
/** Lose ability to exist, take focus and have some text being input */
|
||||
public void disable(){
|
||||
clear();
|
||||
setVisibility(GONE);
|
||||
|
|
@ -150,10 +141,18 @@ public class TouchCharInput extends androidx.appcompat.widget.AppCompatEditText
|
|||
setEnabled(false);
|
||||
}
|
||||
|
||||
/** Send the enter key. */
|
||||
private void sendEnter(){
|
||||
mCharacterSender.sendEnter();
|
||||
clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* This function deals with anything that has to be executed when the constructor is called
|
||||
*/
|
||||
/** Just sets the char sender that should be used. */
|
||||
public void setCharacterSender(CharacterSenderStrategy characterSender){
|
||||
mCharacterSender = characterSender;
|
||||
}
|
||||
|
||||
/** This function deals with anything that has to be executed when the constructor is called */
|
||||
private void setup(){
|
||||
setOnEditorActionListener((textView, i, keyEvent) -> {
|
||||
sendEnter();
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package net.kdt.pojavlaunch.extra;
|
||||
|
||||
public class ExtraConstants {
|
||||
/* ExtraCore constant: a HashMap for converting values such as latest-snapshot or latest-release to actual game version names */
|
||||
public static final String RELEASE_TABLE = "release_table";
|
||||
/* ExtraCore constant: an ArrayList of Strings, where each String is a Minecraft version name */
|
||||
public static final String VERSION_LIST= "lac_version_list";
|
||||
/* ExtraCore constant: Serpent's back button tracking thing */
|
||||
public static final String BACK_PREFERENCE = "back_preference";
|
||||
/* ExtraCore constant: The OPENGL version that should be exposed */
|
||||
public static final String OPEN_GL_VERSION = "open_gl_version";
|
||||
}
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
package net.kdt.pojavlaunch.extra;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
|
||||
/**
|
||||
* Class providing callback across all of a program
|
||||
* to allow easy thread safe implementations of UI update without context leak
|
||||
* It is also perfectly engineered to make it unpleasant to use.
|
||||
*
|
||||
* This class uses a singleton pattern to simplify access to it
|
||||
*/
|
||||
public final class ExtraCore {
|
||||
// No unwanted instantiation
|
||||
private ExtraCore(){}
|
||||
|
||||
// Singleton instance
|
||||
private static ExtraCore sExtraCoreSingleton = null;
|
||||
|
||||
// Store the key-value pair
|
||||
private final Map<String, Object> mValueMap = new ConcurrentHashMap<>();
|
||||
|
||||
// Store what each ExtraListener listen to
|
||||
private final Map<String, ConcurrentLinkedQueue<WeakReference<ExtraListener>>> mListenerMap = new ConcurrentHashMap<>();
|
||||
|
||||
// All public methods will pass through this one
|
||||
private static ExtraCore getInstance(){
|
||||
if(sExtraCoreSingleton == null){
|
||||
synchronized(ExtraCore.class){
|
||||
if(sExtraCoreSingleton == null){
|
||||
sExtraCoreSingleton = new ExtraCore();
|
||||
}
|
||||
}
|
||||
}
|
||||
return sExtraCoreSingleton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the value associated to a key and trigger all listeners
|
||||
* @param key The key
|
||||
* @param value The value
|
||||
*/
|
||||
public static void setValue(String key, Object value){
|
||||
if(value == null || key == null) return; // null values create an NPE on insertion
|
||||
|
||||
getInstance().mValueMap.put(key, value);
|
||||
ConcurrentLinkedQueue<WeakReference<ExtraListener>> extraListenerList = getInstance().mListenerMap.get(key);
|
||||
if(extraListenerList == null) return; //No listeners
|
||||
for(WeakReference<ExtraListener> listener : extraListenerList){
|
||||
if(listener.get() == null){
|
||||
extraListenerList.remove(listener);
|
||||
continue;
|
||||
}
|
||||
|
||||
//Notify the listener about a state change and remove it if asked for
|
||||
if(listener.get().onValueSet(key, value)){
|
||||
ExtraCore.removeExtraListenerFromValue(key, listener.get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** @return The value behind the key */
|
||||
public static Object getValue(String key){
|
||||
return getInstance().mValueMap.get(key);
|
||||
}
|
||||
|
||||
/** Remove the key and its value from the valueMap */
|
||||
public static void removeValue(String key){
|
||||
getInstance().mValueMap.remove(key);
|
||||
}
|
||||
|
||||
/** Remove all values */
|
||||
public static void removeAllValues(){
|
||||
getInstance().mValueMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Link an ExtraListener to a value
|
||||
* @param key The value key to look for
|
||||
* @param listener The ExtraListener to link
|
||||
*/
|
||||
public static void addExtraListener(String key, ExtraListener listener){
|
||||
ConcurrentLinkedQueue<WeakReference<ExtraListener>> listenerList = getInstance().mListenerMap.get(key);
|
||||
// Look for new sets
|
||||
if(listenerList == null){
|
||||
listenerList = new ConcurrentLinkedQueue<>();
|
||||
getInstance().mListenerMap.put(key, listenerList);
|
||||
}
|
||||
|
||||
// This is kinda naive, I should look for duplicates
|
||||
listenerList.add(new WeakReference<>(listener));
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink an ExtraListener from a value.
|
||||
* Unlink null references found along the way
|
||||
* @param key The value key to ignore now
|
||||
* @param listener The ExtraListener to unlink
|
||||
*/
|
||||
public static void removeExtraListenerFromValue(String key, ExtraListener listener){
|
||||
ConcurrentLinkedQueue<WeakReference<ExtraListener>> listenerList = getInstance().mListenerMap.get(key);
|
||||
// Look for new sets
|
||||
if(listenerList == null){
|
||||
listenerList = new ConcurrentLinkedQueue<>();
|
||||
getInstance().mListenerMap.put(key, listenerList);
|
||||
}
|
||||
|
||||
// Removes all occurrences of ExtraListener and all null references
|
||||
for(WeakReference<ExtraListener> listenerWeakReference : listenerList){
|
||||
ExtraListener actualListener = listenerWeakReference.get();
|
||||
|
||||
if(actualListener == null || actualListener == listener){
|
||||
listenerList.remove(listenerWeakReference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlink all ExtraListeners from a value
|
||||
* @param key The key to which ExtraListener are linked
|
||||
*/
|
||||
public static void removeAllExtraListenersFromValue(String key){
|
||||
ConcurrentLinkedQueue<WeakReference<ExtraListener>> listenerList = getInstance().mListenerMap.get(key);
|
||||
// Look for new sets
|
||||
if(listenerList == null){
|
||||
listenerList = new ConcurrentLinkedQueue<>();
|
||||
getInstance().mListenerMap.put(key, listenerList);
|
||||
}
|
||||
|
||||
listenerList.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove all ExtraListeners from listening to any value
|
||||
*/
|
||||
public static void removeAllExtraListeners(){
|
||||
getInstance().mListenerMap.clear();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package net.kdt.pojavlaunch.extra;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
/**
|
||||
* Listener class for the ExtraCore
|
||||
* An ExtraListener can listen to a virtually unlimited amount of values
|
||||
*/
|
||||
public interface ExtraListener<T> {
|
||||
|
||||
/**
|
||||
* Called upon a new value being set
|
||||
* @param key The name of the value
|
||||
* @param value The new value as a string
|
||||
* @return Whether you consume the Listener (stop listening)
|
||||
*/
|
||||
boolean onValueSet(String key, @Nullable T value);
|
||||
|
||||
}
|
||||
|
|
@ -2,6 +2,7 @@ package net.kdt.pojavlaunch.fragments;
|
|||
|
||||
import android.os.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
|
|
@ -12,34 +13,33 @@ import android.graphics.*;
|
|||
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public class ConsoleFragment extends Fragment
|
||||
{
|
||||
public TextView consoleView;
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.lmaintab_consolelog, container, false);
|
||||
public class ConsoleFragment extends Fragment {
|
||||
public TextView mConsoleView;
|
||||
|
||||
consoleView = (TextView) view.findViewById(R.id.lmaintabconsoleLogTextView);
|
||||
consoleView.setTypeface(Typeface.MONOSPACE);
|
||||
consoleView.setHint(this.getText(R.string.main_nolog));
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.fragment_console_log, container, false);
|
||||
|
||||
mConsoleView = (TextView) view.findViewById(R.id.lmaintabconsoleLogTextView);
|
||||
mConsoleView.setTypeface(Typeface.MONOSPACE);
|
||||
mConsoleView.setHint(this.getText(R.string.main_nolog));
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
public void onResume() {
|
||||
super.onResume();
|
||||
consoleView = (TextView) getView().findViewById(R.id.lmaintabconsoleLogTextView);
|
||||
mConsoleView = (TextView) getView().findViewById(R.id.lmaintabconsoleLogTextView);
|
||||
}
|
||||
|
||||
public void putLog(String str) {
|
||||
if (consoleView == null) {
|
||||
consoleView = (TextView) getView().findViewById(R.id.lmaintabconsoleLogTextView);
|
||||
if (mConsoleView == null) {
|
||||
mConsoleView = (TextView) getView().findViewById(R.id.lmaintabconsoleLogTextView);
|
||||
}
|
||||
|
||||
consoleView.append(str);
|
||||
mConsoleView.append(str);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,10 @@ package net.kdt.pojavlaunch.fragments;
|
|||
|
||||
import android.os.*;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import android.util.Log;
|
||||
import android.view.*;
|
||||
import android.widget.*;
|
||||
import java.io.*;
|
||||
|
|
@ -11,80 +14,77 @@ import net.kdt.pojavlaunch.*;
|
|||
import android.graphics.*;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
public class CrashFragment extends Fragment
|
||||
{
|
||||
public static String lastCrashFile = Tools.DIR_DATA + "/lastcrash.txt";
|
||||
|
||||
private String crashContent;
|
||||
private TextView crashView;
|
||||
|
||||
public boolean resetCrashLog = false;
|
||||
|
||||
public static boolean isNewCrash(File crashLog) throws Exception {
|
||||
String content = Tools.read(crashLog.getAbsolutePath());
|
||||
return crashLog != null && content.startsWith("---- Minecraft Crash Report ----");
|
||||
}
|
||||
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
View view = inflater.inflate(R.layout.lmaintab_crashlog, container, false);
|
||||
public class CrashFragment extends Fragment {
|
||||
public static String LAST_CRASH_FILE = Tools.DIR_DATA + "/lastcrash.txt";
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle b)
|
||||
{
|
||||
super.onActivityCreated(b);
|
||||
|
||||
crashView = (TextView) getView().findViewById(R.id.lmaintabconsoleLogCrashTextView);
|
||||
crashView.setTypeface(Typeface.MONOSPACE);
|
||||
crashView.setHint(this.getText(R.string.main_nocrash));
|
||||
private TextView mCrashView;
|
||||
public boolean mResetCrashLog = false;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onResume()
|
||||
{
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
return inflater.inflate(R.layout.fragment_crash_log, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
||||
mCrashView = (TextView) view.findViewById(R.id.lmaintabconsoleLogCrashTextView);
|
||||
mCrashView.setTypeface(Typeface.MONOSPACE);
|
||||
mCrashView.setHint(this.getText(R.string.main_nocrash));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onResume(){
|
||||
super.onResume();
|
||||
refreshCrashFile();
|
||||
}
|
||||
|
||||
public void refreshCrashFile()
|
||||
{
|
||||
|
||||
private void refreshCrashFile() {
|
||||
// Default text
|
||||
mCrashView.setText("");
|
||||
setLastCrash("");
|
||||
|
||||
if(mResetCrashLog) return;
|
||||
|
||||
File crashLog = Tools.lastFileModified(Tools.DIR_HOME_CRASH);
|
||||
String lastCrash = getLastCrash();
|
||||
String crashContent;
|
||||
try {
|
||||
if(!resetCrashLog){
|
||||
File crashLog = Tools.lastFileModified(Tools.DIR_HOME_CRASH);
|
||||
String lastCrash = getLastCrash();
|
||||
if (isNewCrash(crashLog)) {
|
||||
crashContent = Tools.read(crashLog.getAbsolutePath());
|
||||
Tools.write(crashLog.getAbsolutePath(), "\n" + crashContent);
|
||||
setLastCrash(crashLog.getAbsolutePath());
|
||||
crashView.setText(crashContent);
|
||||
} else if(!lastCrash.isEmpty()) {
|
||||
crashContent = Tools.read(lastCrash);
|
||||
crashView.setText(crashContent);
|
||||
} else throw new Exception();
|
||||
} else throw new Exception();
|
||||
} catch (Exception e) {
|
||||
// Can't find crash or no NEW crashes
|
||||
crashView.setText(""/*Log.getStackTraceString(e)*/);
|
||||
setLastCrash("");
|
||||
if (isNewCrash(crashLog)) {
|
||||
crashContent = Tools.read(crashLog.getAbsolutePath());
|
||||
Tools.write(crashLog.getAbsolutePath(), "\n" + crashContent);
|
||||
setLastCrash(crashLog.getAbsolutePath());
|
||||
mCrashView.setText(crashContent);
|
||||
} else if(!lastCrash.isEmpty()) {
|
||||
crashContent = Tools.read(lastCrash);
|
||||
mCrashView.setText(crashContent);
|
||||
}
|
||||
}catch (IOException ioException){
|
||||
Log.e("CrashFragment", ioException.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public void setLastCrash(String newValue) {
|
||||
|
||||
private static boolean isNewCrash(@Nullable File crashLog) throws IOException {
|
||||
if(crashLog == null) return false;
|
||||
String content = Tools.read(crashLog.getAbsolutePath());
|
||||
return content.startsWith("---- Minecraft Crash Report ----");
|
||||
}
|
||||
|
||||
private void setLastCrash(String newValue) {
|
||||
try {
|
||||
Tools.write(lastCrashFile, newValue);
|
||||
} catch (Throwable th) {
|
||||
throw new RuntimeException(th);
|
||||
Tools.write(LAST_CRASH_FILE, newValue);
|
||||
} catch (IOException ioException) {
|
||||
throw new RuntimeException(ioException);
|
||||
}
|
||||
}
|
||||
|
||||
public String getLastCrash() {
|
||||
|
||||
private String getLastCrash() {
|
||||
try {
|
||||
return Tools.read(lastCrashFile);
|
||||
} catch (Throwable th) {
|
||||
return Tools.read(LAST_CRASH_FILE);
|
||||
} catch (IOException ioException) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@ import android.content.Intent;
|
|||
import android.graphics.Color;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.fragment.app.Fragment;
|
||||
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
|
@ -25,74 +25,45 @@ import net.kdt.pojavlaunch.prefs.LauncherPreferences;
|
|||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
|
||||
public class LauncherFragment extends Fragment
|
||||
{
|
||||
private WebView webNews;
|
||||
private View view;
|
||||
private Thread validUrlSelectorThread;
|
||||
private String validChangelog = "/changelog.html";
|
||||
private Handler mainHandler = new Handler(Looper.getMainLooper());
|
||||
private boolean interruptLoad = false;
|
||||
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
view = inflater.inflate(R.layout.lmaintab_news, container, false);
|
||||
return view;
|
||||
}
|
||||
public void selectValidUrl() {
|
||||
String lang = LauncherPreferences.PREF_LANGUAGE;
|
||||
if(lang.equals("default")) lang = Locale.getDefault().getLanguage();
|
||||
final String localizedUrl = "/changelog-"+lang+".html";
|
||||
if(!tryUrl(Tools.URL_HOME+localizedUrl)) return;
|
||||
else {
|
||||
mainHandler.post(()->{
|
||||
interruptLoad = true;
|
||||
validChangelog = localizedUrl;
|
||||
webNews.loadUrl(Tools.URL_HOME+validChangelog);
|
||||
});
|
||||
}
|
||||
}
|
||||
public boolean tryUrl(String url) {
|
||||
Log.i("ChangelogLocale","Trying localized url: "+url);
|
||||
try {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.connect();
|
||||
Log.i("ChangelogLocale","Code: "+conn.getResponseCode());
|
||||
return ("" + conn.getResponseCode()).startsWith("2");
|
||||
}catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
public class LauncherFragment extends Fragment {
|
||||
|
||||
private WebView mNewsWebview;
|
||||
private View mRootView;
|
||||
private Thread mValidUrlSelectorThread;
|
||||
private String mValidChangelog = "/changelog.html";
|
||||
private boolean mInterruptLoad = false;
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(Bundle p1)
|
||||
{
|
||||
super.onActivityCreated(p1);
|
||||
mainHandler = new Handler(Looper.myLooper());
|
||||
webNews = (WebView) getView().findViewById(R.id.lmaintabnewsNewsView);
|
||||
webNews.setWebViewClient(new WebViewClient(){
|
||||
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
|
||||
super.onCreateView(inflater, container, savedInstanceState);
|
||||
mRootView = inflater.inflate(R.layout.fragment_news, container, false);
|
||||
return mRootView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
mNewsWebview = (WebView) getView().findViewById(R.id.lmaintabnewsNewsView);
|
||||
mNewsWebview.setWebViewClient(new WebViewClient(){
|
||||
|
||||
// API < 23
|
||||
@Override
|
||||
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
|
||||
Log.i("WebNews",failingUrl + ": "+description);
|
||||
if(webNews != null){
|
||||
if(validUrlSelectorThread.isAlive()) validUrlSelectorThread.interrupt();
|
||||
removeWebView();
|
||||
//Change the background to match the other pages.
|
||||
//We change it only when the webView is removed to avoid huge overdraw.
|
||||
LauncherFragment.this.view.setBackgroundColor(Color.parseColor("#44000000"));
|
||||
}
|
||||
if(mNewsWebview == null) return;
|
||||
|
||||
if(mValidUrlSelectorThread.isAlive()) mValidUrlSelectorThread.interrupt();
|
||||
removeWebView();
|
||||
//Change the background to match the other pages.
|
||||
//We change it only when the webView is removed to avoid huge overdraw.
|
||||
LauncherFragment.this.mRootView.setBackgroundColor(Color.parseColor("#44000000"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||
if(!url.equals(Tools.URL_HOME + validChangelog)){
|
||||
if(!url.equals(Tools.URL_HOME + mValidChangelog)){
|
||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
|
||||
startActivity(i);
|
||||
return true;
|
||||
|
|
@ -104,17 +75,17 @@ public class LauncherFragment extends Fragment
|
|||
@Override
|
||||
public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {
|
||||
Log.i("WebNews",error.getDescription()+"");
|
||||
if(webNews != null){
|
||||
if(validUrlSelectorThread.isAlive()) validUrlSelectorThread.interrupt();
|
||||
removeWebView();
|
||||
LauncherFragment.this.view.setBackgroundColor(Color.parseColor("#44000000"));
|
||||
}
|
||||
if(mNewsWebview == null) return;
|
||||
|
||||
if(mValidUrlSelectorThread.isAlive()) mValidUrlSelectorThread.interrupt();
|
||||
removeWebView();
|
||||
LauncherFragment.this.mRootView.setBackgroundColor(Color.parseColor("#44000000"));
|
||||
}
|
||||
|
||||
@RequiresApi(23)
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
if(!request.getUrl().toString().equals(Tools.URL_HOME + validChangelog)){
|
||||
if(!request.getUrl().toString().equals(Tools.URL_HOME + mValidChangelog)){
|
||||
Intent i = new Intent(Intent.ACTION_VIEW, request.getUrl());
|
||||
startActivity(i);
|
||||
return true;
|
||||
|
|
@ -122,36 +93,64 @@ public class LauncherFragment extends Fragment
|
|||
return false;
|
||||
}
|
||||
});
|
||||
webNews.clearCache(true);
|
||||
webNews.getSettings().setJavaScriptEnabled(true);
|
||||
validUrlSelectorThread = new Thread(this::selectValidUrl);
|
||||
validUrlSelectorThread.start();
|
||||
if(!interruptLoad)webNews.loadUrl(Tools.URL_HOME + validChangelog);
|
||||
|
||||
mNewsWebview.clearCache(true);
|
||||
mNewsWebview.getSettings().setJavaScriptEnabled(true);
|
||||
mValidUrlSelectorThread = new Thread(this::selectValidUrl);
|
||||
mValidUrlSelectorThread.start();
|
||||
if(!mInterruptLoad) mNewsWebview.loadUrl(Tools.URL_HOME + mValidChangelog);
|
||||
}
|
||||
|
||||
private void selectValidUrl() {
|
||||
String lang = LauncherPreferences.PREF_LANGUAGE;
|
||||
if(lang.equals("default")) lang = Locale.getDefault().getLanguage();
|
||||
final String localizedUrl = "/changelog-" + lang + ".html";
|
||||
|
||||
if(!tryUrl(Tools.URL_HOME+localizedUrl)) return;
|
||||
|
||||
requireActivity().runOnUiThread(() -> {
|
||||
mInterruptLoad = true;
|
||||
mValidChangelog = localizedUrl;
|
||||
mNewsWebview.loadUrl(Tools.URL_HOME + mValidChangelog);
|
||||
});
|
||||
}
|
||||
|
||||
private boolean tryUrl(String url) {
|
||||
Log.i("ChangelogLocale","Trying localized url: "+url);
|
||||
try {
|
||||
HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
|
||||
conn.connect();
|
||||
Log.i("ChangelogLocale","Code: "+conn.getResponseCode());
|
||||
return ("" + conn.getResponseCode()).startsWith("2");
|
||||
}catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void removeWebView() {
|
||||
//Removing the parent which contain the webView crashes the viewPager.
|
||||
//So I just try to "minimize" its impact on memory instead
|
||||
|
||||
webNews.clearHistory();
|
||||
webNews.clearCache(true);
|
||||
mNewsWebview.clearHistory();
|
||||
mNewsWebview.clearCache(true);
|
||||
|
||||
// Loading a blank page is optional, but will ensure that the WebView isn't doing anything when you destroy it.
|
||||
webNews.loadUrl("about:blank");
|
||||
mNewsWebview.loadUrl("about:blank");
|
||||
|
||||
webNews.onPause();
|
||||
webNews.removeAllViews();
|
||||
webNews.destroyDrawingCache();
|
||||
mNewsWebview.onPause();
|
||||
mNewsWebview.removeAllViews();
|
||||
mNewsWebview.destroyDrawingCache();
|
||||
|
||||
// make sure to call webNews.resumeTimers().
|
||||
webNews.pauseTimers();
|
||||
mNewsWebview.pauseTimers();
|
||||
|
||||
webNews.setVisibility(View.GONE);
|
||||
mNewsWebview.setVisibility(View.GONE);
|
||||
|
||||
webNews.destroy();
|
||||
mNewsWebview.destroy();
|
||||
|
||||
// Null out the reference so that you don't end up re-using it.
|
||||
webNews = null;
|
||||
mNewsWebview = null;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue