mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-21 00:18:18 +00:00
Compare commits
16
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1658072b1a | ||
|
|
b6e88b20cb | ||
|
|
f4af50b2cb | ||
|
|
00c47f4c70 | ||
|
|
11649a236a | ||
|
|
ff712196cc | ||
|
|
6c57f3f261 | ||
|
|
626f8d00d1 | ||
|
|
45ecc71749 | ||
|
|
3d60bd3d53 | ||
|
|
e40af4c49b | ||
|
|
e0a31694af | ||
|
|
c8994c2484 | ||
|
|
35c8428f08 | ||
|
|
bca761f26a | ||
|
|
79c06ca629 |
No files matched your search
+9
-12
@@ -1,5 +1,4 @@
|
||||
import groovy.json.JsonSlurper
|
||||
import java.io.IOException
|
||||
import java.net.URI
|
||||
import java.security.MessageDigest
|
||||
import java.util.zip.ZipInputStream
|
||||
@@ -16,7 +15,7 @@ plugins {
|
||||
id("org.jetbrains.kotlin.plugin.compose")
|
||||
}
|
||||
|
||||
val currentGeodeVersion = "5.10.1"
|
||||
val currentGeodeVersion = "5.11.2"
|
||||
|
||||
abstract class DownloadVersionMetadataTask : DefaultTask() {
|
||||
@get:Input
|
||||
@@ -248,9 +247,9 @@ android {
|
||||
defaultConfig {
|
||||
applicationId = "com.geode.launcher"
|
||||
minSdk = 23
|
||||
targetSdk = 36
|
||||
versionCode = 30
|
||||
versionName = "1.8.0"
|
||||
targetSdk = 37
|
||||
versionCode = 31
|
||||
versionName = "1.9.0"
|
||||
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
@@ -318,7 +317,6 @@ android {
|
||||
buildFeatures {
|
||||
compose = true
|
||||
buildConfig = true
|
||||
prefab = true
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
@@ -332,7 +330,7 @@ android {
|
||||
}
|
||||
}
|
||||
namespace = "com.geode.launcher"
|
||||
ndkVersion = "29.0.14206865"
|
||||
ndkVersion = "30.0.16248370"
|
||||
}
|
||||
|
||||
androidComponents {
|
||||
@@ -349,8 +347,8 @@ androidComponents {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation (platform("androidx.compose:compose-bom:2026.08.00"))
|
||||
implementation ("androidx.core:core-ktx:1.19.0")
|
||||
implementation (platform("androidx.compose:compose-bom:2026.09.00"))
|
||||
implementation ("androidx.core:core-ktx:1.19.1")
|
||||
implementation ("androidx.compose.ui:ui")
|
||||
implementation ("androidx.compose.material3:material3")
|
||||
implementation ("androidx.compose.ui:ui-tooling-preview")
|
||||
@@ -360,7 +358,6 @@ dependencies {
|
||||
implementation ("androidx.activity:activity-ktx:1.13.0")
|
||||
implementation ("androidx.appcompat:appcompat:1.8.0")
|
||||
implementation ("androidx.documentfile:documentfile:1.1.0")
|
||||
implementation ("androidx.games:games-frame-pacing:2.1.3")
|
||||
|
||||
implementation ("com.squareup.okio:okio:3.18.2")
|
||||
implementation ("com.squareup.okhttp3:okhttp:5.5.0")
|
||||
@@ -370,8 +367,8 @@ dependencies {
|
||||
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.11.0")
|
||||
implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
|
||||
|
||||
implementation ("com.mikepenz:multiplatform-markdown-renderer-android:0.45.0")
|
||||
implementation ("com.mikepenz:multiplatform-markdown-renderer-m3:0.45.0")
|
||||
implementation ("com.mikepenz:multiplatform-markdown-renderer-android:0.46.0")
|
||||
implementation ("com.mikepenz:multiplatform-markdown-renderer-m3:0.46.0")
|
||||
implementation ("androidx.browser:browser:1.10.0")
|
||||
debugImplementation ("androidx.compose.ui:ui-tooling")
|
||||
coreLibraryDesugaring ("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||
|
||||
@@ -125,7 +125,7 @@
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".GeometryDashActivity"
|
||||
android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard"
|
||||
android:configChanges="fontScale|layoutDirection|density|smallestScreenSize|screenSize|uiMode|screenLayout|orientation|navigation|keyboardHidden|keyboard|locale"
|
||||
android:exported="true"
|
||||
android:hardwareAccelerated="true"
|
||||
android:screenOrientation="landscape"
|
||||
@@ -233,6 +233,26 @@
|
||||
<data android:scheme="geode-launcher" android:host="main" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
<activity-alias
|
||||
android:name=".MainActivityGreen"
|
||||
android:enabled="false"
|
||||
android:exported="true"
|
||||
android:icon="@mipmap/ic_launcher_green"
|
||||
android:label="@string/app_name"
|
||||
android:targetActivity=".MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="geode-launcher" android:host="main" />
|
||||
</intent-filter>
|
||||
</activity-alias>
|
||||
|
||||
<provider
|
||||
android:name=".UserDirectoryProvider"
|
||||
|
||||
@@ -2,9 +2,7 @@ cmake_minimum_required(VERSION 3.21)
|
||||
|
||||
project(launcherfix)
|
||||
|
||||
find_package(games-frame-pacing REQUIRED CONFIG)
|
||||
|
||||
add_library(launcherfix SHARED launcher-fix.cpp patcher.cpp pacing.cpp)
|
||||
add_library(launcherfix SHARED launcher-fix.cpp patcher.cpp)
|
||||
|
||||
target_compile_features(launcherfix PUBLIC cxx_std_20)
|
||||
set_target_properties(launcherfix PROPERTIES CXX_EXTENSIONS OFF)
|
||||
@@ -16,5 +14,4 @@ set_target_properties(launcherfix PROPERTIES
|
||||
CXX_VISIBILITY_PRESET hidden
|
||||
)
|
||||
|
||||
target_link_libraries(launcherfix PRIVATE log android)
|
||||
target_link_libraries(launcherfix PRIVATE games-frame-pacing::swappy_static)
|
||||
target_link_libraries(launcherfix PRIVATE log)
|
||||
@@ -1,65 +0,0 @@
|
||||
#include <swappy/swappyGL.h>
|
||||
#include <swappy/swappyGL_extra.h>
|
||||
#include <android/native_window_jni.h>
|
||||
#include <memory>
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_initFramePacing(
|
||||
JNIEnv *env,
|
||||
jobject,
|
||||
jobject activity
|
||||
) {
|
||||
SwappyGL_init(env, activity);
|
||||
}
|
||||
|
||||
struct NativeWindowDeleter {
|
||||
void operator()(ANativeWindow* p) const {
|
||||
if (p != nullptr) ANativeWindow_release(p);
|
||||
}
|
||||
};
|
||||
|
||||
std::unique_ptr<ANativeWindow, NativeWindowDeleter> native_window = nullptr;
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_destroyFramePacing(JNIEnv*, jobject) {
|
||||
SwappyGL_destroy();
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT jboolean JNICALL Java_com_geode_launcher_LauncherFix_swapFrame(
|
||||
JNIEnv*,
|
||||
jobject,
|
||||
jlong display,
|
||||
jlong surface
|
||||
) {
|
||||
return SwappyGL_swap(
|
||||
reinterpret_cast<EGLDisplay>(display),
|
||||
reinterpret_cast<EGLSurface>(surface)
|
||||
);
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setSurface(
|
||||
JNIEnv* env,
|
||||
jobject,
|
||||
jobject surface
|
||||
) {
|
||||
if (surface == nullptr) {
|
||||
native_window.reset();
|
||||
SwappyGL_setWindow(nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
native_window.reset(ANativeWindow_fromSurface(env, surface));
|
||||
SwappyGL_setWindow(native_window.get());
|
||||
}
|
||||
|
||||
extern "C"
|
||||
JNIEXPORT void JNICALL Java_com_geode_launcher_LauncherFix_setSwapInterval(
|
||||
JNIEnv* env,
|
||||
jobject,
|
||||
jlong interval_ns
|
||||
) {
|
||||
SwappyGL_setAutoSwapInterval(false);
|
||||
SwappyGL_setSwapIntervalNS(interval_ns);
|
||||
}
|
||||
@@ -2,33 +2,106 @@ package com.customRobTop
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.*
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.ClipData
|
||||
import android.content.ClipboardManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.OpenableColumns
|
||||
import android.provider.Settings
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.net.toUri
|
||||
import com.customRobTop.contracts.ImportFileActivityContract
|
||||
import com.customRobTop.contracts.ShareFileActivityContract
|
||||
import com.geode.launcher.R
|
||||
import com.geode.launcher.utils.DownloadUtils
|
||||
import com.geode.launcher.utils.LaunchUtils
|
||||
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
|
||||
import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.closeIMEKeyboard
|
||||
import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.openIMEKeyboard
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.IOException
|
||||
import java.io.OutputStream
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
import java.util.UUID
|
||||
|
||||
@Suppress("unused", "UNUSED_PARAMETER")
|
||||
object BaseRobTopActivity {
|
||||
const val KEYBOARD_INPUT_MULTILINE = 1
|
||||
const val KEYBOARD_INPUT_NUMERIC = 4
|
||||
const val KEYBOARD_INPUT_PASSWORD = 2
|
||||
const val LEVEL_EXTENSION: String = ".gdlvl"
|
||||
|
||||
private var isLoaded = false
|
||||
var blockBackButton = false
|
||||
private var keyboardActive = false
|
||||
var curKeyboardInputFlags = 0
|
||||
|
||||
var isPaused = false
|
||||
|
||||
lateinit var me: WeakReference<Activity>
|
||||
private var shouldResumeSound = true
|
||||
|
||||
fun setCurrentActivity(currentActivity: Activity) {
|
||||
private var pendingExportPaths = emptyList<String>()
|
||||
|
||||
private lateinit var importFileActivityContract: ActivityResultLauncher<Unit>
|
||||
private lateinit var shareFileActivityContract: ActivityResultLauncher<String>
|
||||
private lateinit var shareFolderActivityContract: ActivityResultLauncher<Uri?>
|
||||
|
||||
fun setCurrentActivity(currentActivity: AppCompatActivity) {
|
||||
me = WeakReference(currentActivity)
|
||||
|
||||
importFileActivityContract = currentActivity.registerForActivityResult(ImportFileActivityContract()) { uris ->
|
||||
val filePaths = copyPickedFiles(uris)
|
||||
|
||||
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.filesPicked(filePaths.toTypedArray())
|
||||
}
|
||||
}
|
||||
|
||||
shareFileActivityContract = currentActivity.registerForActivityResult(ShareFileActivityContract()) { uri ->
|
||||
val currentlyPending = pendingExportPaths
|
||||
pendingExportPaths = emptyList()
|
||||
|
||||
val exportPath = currentlyPending.firstOrNull()
|
||||
if (exportPath == null || uri == null) {
|
||||
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.fileShareFinished(false)
|
||||
}
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val result = saveToDocument(exportPath, uri)
|
||||
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.fileShareFinished(result)
|
||||
}
|
||||
}
|
||||
|
||||
shareFolderActivityContract = currentActivity.registerForActivityResult(ActivityResultContracts.OpenDocumentTree()) { uri ->
|
||||
val currentlyPending = pendingExportPaths
|
||||
pendingExportPaths = emptyList()
|
||||
|
||||
if (uri == null) {
|
||||
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.fileShareFinished(false)
|
||||
}
|
||||
return@registerForActivityResult
|
||||
}
|
||||
|
||||
val result = saveToFolder(currentlyPending, uri) > 0
|
||||
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.fileShareFinished(result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("HardwareIds")
|
||||
@@ -87,6 +160,18 @@ object BaseRobTopActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getClipboardString(): String {
|
||||
me.get()?.run {
|
||||
val manager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
||||
val clip = manager.primaryClip
|
||||
if (clip != null && clip.itemCount > 0) {
|
||||
return clip.getItemAt(0).coerceToText(this).toString()
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun sendMail(subject: String, body: String, to: String) {
|
||||
me.get()?.runOnUiThread {
|
||||
@@ -264,4 +349,164 @@ object BaseRobTopActivity {
|
||||
level = DeprecationLevel.HIDDEN
|
||||
)
|
||||
fun logEvent(event: String) {}
|
||||
|
||||
@JvmStatic
|
||||
fun setKeyboardInputFlags(flags: Int) {
|
||||
curKeyboardInputFlags = flags
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun pickImportFiles() {
|
||||
try {
|
||||
importFileActivityContract.launch(Unit)
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
me.get()?.apply {
|
||||
runOnUiThread {
|
||||
Toast.makeText(
|
||||
this,
|
||||
R.string.no_activity_found,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun copyPickedFiles(uris: List<Uri>): List<String> {
|
||||
val me = me.get() ?: return emptyList()
|
||||
|
||||
val importsDir = File(LaunchUtils.getSaveDirectory(me), "imports")
|
||||
importsDir.mkdirs()
|
||||
|
||||
return uris.mapIndexedNotNull { index, uri ->
|
||||
val displayName = displayName(uri)
|
||||
?: "import_$index$LEVEL_EXTENSION"
|
||||
if (!displayName.endsWith(LEVEL_EXTENSION)) {
|
||||
return@mapIndexedNotNull null
|
||||
}
|
||||
|
||||
var outputFile = File(importsDir, displayName)
|
||||
var counter = 2
|
||||
while (outputFile.exists()) {
|
||||
outputFile = File(importsDir, "${counter}_$displayName")
|
||||
counter++
|
||||
}
|
||||
|
||||
try {
|
||||
me.contentResolver.openInputStream(uri)?.use { input ->
|
||||
outputFile.outputStream().use { output ->
|
||||
DownloadUtils.copyFile(input, output)
|
||||
}
|
||||
|
||||
outputFile.absolutePath
|
||||
}
|
||||
} catch (_: Exception) {
|
||||
outputFile.delete()
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayName(uri: Uri): String? {
|
||||
me.get()?.contentResolver?.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { cursorQuery ->
|
||||
if (cursorQuery.moveToFirst()) {
|
||||
val displayName = cursorQuery.getString(0)
|
||||
if (displayName.isNotEmpty()) {
|
||||
return File(displayName).name
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun shareFiles(files: Array<String>) {
|
||||
pendingExportPaths = files.toList()
|
||||
|
||||
try {
|
||||
if (files.size == 1) {
|
||||
shareFileActivityContract.launch(files.first())
|
||||
return
|
||||
} else if (files.size > 1) {
|
||||
shareFolderActivityContract.launch(null)
|
||||
return
|
||||
}
|
||||
} catch (_: ActivityNotFoundException) {
|
||||
me.get()?.apply {
|
||||
runOnUiThread {
|
||||
Toast.makeText(
|
||||
this,
|
||||
R.string.no_activity_found,
|
||||
Toast.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
}
|
||||
|
||||
Cocos2dxGLSurfaceView.cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.fileShareFinished(false)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun saveToFolder(exportPaths: List<String>, treeUri: Uri): Int {
|
||||
val uri = DocumentsContract.buildDocumentUriUsingTree(treeUri, DocumentsContract.getTreeDocumentId(treeUri))
|
||||
|
||||
val me = me.get() ?: return 0
|
||||
|
||||
return exportPaths.count { path ->
|
||||
val filename = File(path).name
|
||||
val documentUri = DocumentsContract.createDocument(me.contentResolver, uri, "application/octet-stream", filename)
|
||||
?: return@count false
|
||||
|
||||
saveToDocument(path, documentUri)
|
||||
}
|
||||
}
|
||||
|
||||
private fun saveToDocument(exportPath: String, uri: Uri): Boolean {
|
||||
try {
|
||||
FileInputStream(exportPath).use { inputStream ->
|
||||
openOutput(uri).use { outputStream ->
|
||||
if (outputStream != null) {
|
||||
DownloadUtils.copyFile(inputStream, outputStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ensureLevelExtension(uri)
|
||||
return true
|
||||
} catch (_: IOException) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private fun openOutput(uri: Uri): OutputStream? {
|
||||
val me = me.get() ?: return null
|
||||
return try {
|
||||
me.contentResolver.openOutputStream(uri, "wt")
|
||||
} catch (_: IOException) {
|
||||
me.contentResolver.openOutputStream(uri)
|
||||
}
|
||||
}
|
||||
|
||||
private fun ensureLevelExtension(uri: Uri) {
|
||||
val displayName = displayName(uri)
|
||||
if (displayName == null || displayName.endsWith(LEVEL_EXTENSION)) {
|
||||
return
|
||||
}
|
||||
val me = me.get() ?: return
|
||||
|
||||
val extensionIdx = displayName.lastIndexOf(".gdlvl.")
|
||||
val substring = if (extensionIdx < 0) {
|
||||
displayName + LEVEL_EXTENSION
|
||||
} else {
|
||||
displayName.substring(0, extensionIdx + 6)
|
||||
}
|
||||
|
||||
try {
|
||||
DocumentsContract.renameDocument(me.contentResolver, uri, substring)
|
||||
} catch (_: IOException) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -55,4 +55,16 @@ object JniToCpp {
|
||||
|
||||
@JvmStatic
|
||||
external fun videoAdShowed()
|
||||
|
||||
@JvmStatic
|
||||
external fun filesPicked(files: Array<String>)
|
||||
|
||||
@JvmStatic
|
||||
external fun fileShareFinished(success: Boolean)
|
||||
|
||||
@JvmStatic
|
||||
external fun textInputCaretToEnd()
|
||||
|
||||
@JvmStatic
|
||||
external fun textInputDone()
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.customRobTop.contracts
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
|
||||
class ImportFileActivityContract : ActivityResultContract<Unit, List<Uri>>() {
|
||||
override fun createIntent(
|
||||
context: Context,
|
||||
input: Unit
|
||||
): Intent {
|
||||
return Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/*"
|
||||
putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true)
|
||||
}
|
||||
}
|
||||
|
||||
override fun parseResult(
|
||||
resultCode: Int,
|
||||
intent: Intent?
|
||||
): List<Uri> {
|
||||
if (resultCode != Activity.RESULT_OK || intent == null) {
|
||||
return emptyList()
|
||||
}
|
||||
|
||||
val clipData = intent.clipData
|
||||
return if (clipData != null) {
|
||||
List(clipData.itemCount) { i -> clipData.getItemAt(i).uri }
|
||||
} else {
|
||||
val intentData = intent.data
|
||||
if (intentData != null)
|
||||
listOf(intentData)
|
||||
else
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.customRobTop.contracts
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.provider.DocumentsContract
|
||||
import androidx.activity.result.contract.ActivityResultContract
|
||||
import java.io.File
|
||||
|
||||
class ShareFileActivityContract : ActivityResultContract<String, Uri?>() {
|
||||
override fun createIntent(
|
||||
context: Context,
|
||||
input: String
|
||||
): Intent {
|
||||
return Intent(Intent.ACTION_CREATE_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "application/octet-stream"
|
||||
putExtra(Intent.EXTRA_TITLE, File(input).name)
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
putExtra(DocumentsContract.EXTRA_INITIAL_URI,
|
||||
DocumentsContract.buildDocumentUri("com.android.externalstorage.documents", "primary:Download")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun parseResult(
|
||||
resultCode: Int,
|
||||
intent: Intent?
|
||||
): Uri? {
|
||||
if (resultCode != Activity.RESULT_OK || intent == null) {
|
||||
return null
|
||||
}
|
||||
|
||||
return intent.data
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -5,9 +5,7 @@ import android.content.Intent
|
||||
import android.content.pm.ActivityInfo
|
||||
import android.content.pm.ApplicationInfo
|
||||
import android.content.pm.PackageInfo
|
||||
import android.opengl.EGL14
|
||||
import android.opengl.EGLConfig
|
||||
import android.opengl.EGLDisplay
|
||||
import android.opengl.GLSurfaceView
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
@@ -45,6 +43,9 @@ import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.io.IOException
|
||||
import javax.microedition.khronos.egl.EGL10
|
||||
import javax.microedition.khronos.egl.EGLConfig
|
||||
import javax.microedition.khronos.egl.EGLDisplay
|
||||
|
||||
enum class DisplayMode {
|
||||
DEFAULT, LIMITED, FULLSCREEN;
|
||||
@@ -77,7 +78,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
private var mAspectRatio = 0.0f
|
||||
private var mScreenZoom = 1.0f
|
||||
private var mScreenZoomFit = false
|
||||
private var mNativeFramePacing = false
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
setupUIState()
|
||||
@@ -131,10 +131,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
}
|
||||
})
|
||||
mGLSurfaceView?.manualBackEvents = true
|
||||
|
||||
if (mNativeFramePacing) {
|
||||
LauncherFix.initFramePacing(this)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createVersionFile() {
|
||||
@@ -175,7 +171,8 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME)
|
||||
tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME)
|
||||
|
||||
if (GamePackageUtils.getGameVersionCode(packageManager) >= 39L) {
|
||||
val gameVersionCode = GamePackageUtils.getGameVersionCode(packageManager)
|
||||
if (gameVersionCode >= 39L) {
|
||||
// this fix requires geode v3, which is 2.206+
|
||||
// there is a short period in which 2.206 users will still have geode v2, but whatever. ig
|
||||
LauncherFix.enableExceptionsRenaming()
|
||||
@@ -186,6 +183,9 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
loadInternalMods()
|
||||
|
||||
val baseView = createView()
|
||||
if (gameVersionCode >= 42L) {
|
||||
mGLSurfaceView?.sendNewCocosEvents = true
|
||||
}
|
||||
|
||||
if (!GeodeUtils.handleSafeArea && Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
|
||||
val frameLayoutParams = ViewGroup.LayoutParams(
|
||||
@@ -209,12 +209,22 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
left = 0, top = 0, right = 0, bottom = 0
|
||||
)
|
||||
}
|
||||
|
||||
Cocos2dxGLSurfaceView.onKeyboardInsets(insets)
|
||||
|
||||
WindowInsets.CONSUMED
|
||||
}
|
||||
|
||||
frameLayout.addView(baseView)
|
||||
setContentView(frameLayout)
|
||||
} else {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
baseView.setOnApplyWindowInsetsListener { view, insets ->
|
||||
Cocos2dxGLSurfaceView.onKeyboardInsets(insets)
|
||||
return@setOnApplyWindowInsetsListener view.onApplyWindowInsets(insets)
|
||||
}
|
||||
}
|
||||
|
||||
setContentView(baseView)
|
||||
}
|
||||
|
||||
@@ -495,14 +505,16 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
|
||||
val frameRate = mLimitedRefreshRate
|
||||
if (frameRate != null) {
|
||||
glSurfaceView.setMaxFrameRate(frameRate)
|
||||
renderer.limitFrameRate(frameRate)
|
||||
}
|
||||
|
||||
glSurfaceView.nativeFramePacing = mNativeFramePacing
|
||||
|
||||
editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
|
||||
glSurfaceView.cocos2dxEditText = editText
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||
glSurfaceView.defaultFocusHighlightEnabled = false
|
||||
}
|
||||
|
||||
return frameLayout
|
||||
}
|
||||
|
||||
@@ -535,8 +547,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
}
|
||||
}
|
||||
|
||||
mNativeFramePacing = preferenceUtils.getBoolean(PreferenceUtils.Key.NATIVE_FRAME_PACING)
|
||||
|
||||
if (displayMode == DisplayMode.FULLSCREEN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
|
||||
GeodeUtils.handleSafeArea = true
|
||||
@@ -570,10 +580,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
FMOD.close()
|
||||
|
||||
if (mNativeFramePacing) {
|
||||
LauncherFix.destroyFramePacing()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resumeGame() {
|
||||
@@ -745,7 +751,11 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
}
|
||||
}
|
||||
|
||||
class EGLConfigChooser : ExplicitGLSurfaceView.EGLConfigChooser {
|
||||
class EGLConfigChooser : GLSurfaceView.EGLConfigChooser {
|
||||
// this comes from EGL14, but is unavailable on EGL10
|
||||
// also EGL14 is incompatible with EGL10. so whatever
|
||||
private var EGL_OPENGL_ES2_BIT: Int = 0x04
|
||||
|
||||
private data class ConfigValues(
|
||||
val redSize: Int,
|
||||
val greenSize: Int,
|
||||
@@ -755,29 +765,29 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
val stencilSize: Int,
|
||||
)
|
||||
|
||||
private fun getAttribValue(display: EGLDisplay, config: EGLConfig, attrib: Int): Int {
|
||||
private fun getAttribValue(egl: EGL10, display: EGLDisplay, config: EGLConfig, attrib: Int): Int {
|
||||
val value = IntArray(1)
|
||||
EGL14.eglGetConfigAttrib(display, config, attrib, value, 0)
|
||||
egl.eglGetConfigAttrib(display, config, attrib, value)
|
||||
|
||||
return value[0]
|
||||
}
|
||||
|
||||
private fun testConfig(display: EGLDisplay, attrib: ConfigValues): EGLConfig? {
|
||||
private fun testConfig(egl: EGL10, display: EGLDisplay, attrib: ConfigValues): EGLConfig? {
|
||||
val attribList = intArrayOf(
|
||||
EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
|
||||
EGL14.EGL_RED_SIZE, attrib.redSize,
|
||||
EGL14.EGL_GREEN_SIZE, attrib.greenSize,
|
||||
EGL14.EGL_BLUE_SIZE, attrib.blueSize,
|
||||
EGL14.EGL_ALPHA_SIZE, attrib.alphaSize,
|
||||
EGL14.EGL_DEPTH_SIZE, attrib.depthSize,
|
||||
EGL14.EGL_STENCIL_SIZE, attrib.stencilSize,
|
||||
EGL14.EGL_NONE
|
||||
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,
|
||||
EGL10.EGL_RED_SIZE, attrib.redSize,
|
||||
EGL10.EGL_GREEN_SIZE, attrib.greenSize,
|
||||
EGL10.EGL_BLUE_SIZE, attrib.blueSize,
|
||||
EGL10.EGL_ALPHA_SIZE, attrib.alphaSize,
|
||||
EGL10.EGL_DEPTH_SIZE, attrib.depthSize,
|
||||
EGL10.EGL_STENCIL_SIZE, attrib.stencilSize,
|
||||
EGL10.EGL_NONE
|
||||
)
|
||||
|
||||
val configs = arrayOfNulls<EGLConfig>(1)
|
||||
val numConfigs = IntArray(1)
|
||||
|
||||
val res = EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.size, numConfigs, 0)
|
||||
val res = egl.eglChooseConfig(display, attribList, configs, configs.size, numConfigs)
|
||||
if (res && numConfigs[0] > 0 && configs[0] != null) {
|
||||
return configs[0]
|
||||
}
|
||||
@@ -785,18 +795,18 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
return null
|
||||
}
|
||||
|
||||
private fun printConfig(display: EGLDisplay, config: EGLConfig) {
|
||||
val id = getAttribValue(display, config, EGL14.EGL_CONFIG_ID)
|
||||
val red = getAttribValue(display, config, EGL14.EGL_RED_SIZE)
|
||||
val green = getAttribValue(display, config, EGL14.EGL_GREEN_SIZE)
|
||||
val blue = getAttribValue(display, config, EGL14.EGL_BLUE_SIZE)
|
||||
val alpha = getAttribValue(display, config, EGL14.EGL_ALPHA_SIZE)
|
||||
val depth = getAttribValue(display, config, EGL14.EGL_DEPTH_SIZE)
|
||||
val stencil = getAttribValue(display, config, EGL14.EGL_STENCIL_SIZE)
|
||||
private fun printConfig(egl: EGL10, display: EGLDisplay, config: EGLConfig) {
|
||||
val id = getAttribValue(egl, display, config, EGL10.EGL_CONFIG_ID)
|
||||
val red = getAttribValue(egl, display, config, EGL10.EGL_RED_SIZE)
|
||||
val green = getAttribValue(egl, display, config, EGL10.EGL_GREEN_SIZE)
|
||||
val blue = getAttribValue(egl, display, config, EGL10.EGL_BLUE_SIZE)
|
||||
val alpha = getAttribValue(egl, display, config, EGL10.EGL_ALPHA_SIZE)
|
||||
val depth = getAttribValue(egl, display, config, EGL10.EGL_DEPTH_SIZE)
|
||||
val stencil = getAttribValue(egl, display, config, EGL10.EGL_STENCIL_SIZE)
|
||||
println("EGLConfig $id: (red = $red, green = $green, blue = $blue, alpha = $alpha, depth = $depth, stencil = $stencil)")
|
||||
}
|
||||
|
||||
override fun chooseConfig(display: EGLDisplay): EGLConfig {
|
||||
override fun chooseConfig(egl: EGL10, display: EGLDisplay): EGLConfig {
|
||||
val configs = listOf(
|
||||
ConfigValues(8, 8, 8, 8, 16, 8),
|
||||
ConfigValues(8, 8, 8, 0, 16, 8),
|
||||
@@ -805,9 +815,9 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
||||
)
|
||||
|
||||
return configs.firstNotNullOf {
|
||||
val config = testConfig(display, it)
|
||||
val config = testConfig(egl, display, it)
|
||||
if (config != null) {
|
||||
printConfig(display, config)
|
||||
printConfig(egl, display, config)
|
||||
}
|
||||
|
||||
config
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package com.geode.launcher
|
||||
|
||||
import android.app.Activity
|
||||
import android.view.Surface
|
||||
import androidx.annotation.Keep
|
||||
import com.geode.launcher.utils.Constants
|
||||
|
||||
@@ -18,10 +16,4 @@ object LauncherFix {
|
||||
external fun enableExceptionsRenaming()
|
||||
|
||||
external fun performPatches()
|
||||
|
||||
external fun initFramePacing(activity: Activity)
|
||||
external fun destroyFramePacing()
|
||||
external fun swapFrame(display: Long, surface: Long): Boolean
|
||||
external fun setSurface(surface: Surface?)
|
||||
external fun setSwapInterval(intervalNs: Long)
|
||||
}
|
||||
@@ -104,8 +104,10 @@ class MainActivity : ComponentActivity() {
|
||||
val gdInstalled = GamePackageUtils.isGameInstalled(packageManager)
|
||||
val geodeInstalled = LaunchUtils.isGeodeInstalled(this)
|
||||
|
||||
if (gdInstalled && geodeInstalled && intent.getBooleanExtra("restarted", false)) {
|
||||
onLaunch(this)
|
||||
val restartImmediately = intent.getBooleanExtra("restarted", false)
|
||||
if (gdInstalled && geodeInstalled && restartImmediately) {
|
||||
val withSafeMode = intent.getBooleanExtra("safe-mode", false)
|
||||
onLaunch(this, withSafeMode)
|
||||
}
|
||||
|
||||
enableEdgeToEdge()
|
||||
|
||||
@@ -10,7 +10,10 @@ import android.provider.DocumentsContract
|
||||
import android.provider.DocumentsProvider
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.geode.launcher.utils.ApplicationIcon
|
||||
import com.geode.launcher.utils.IconUtils
|
||||
import com.geode.launcher.utils.LaunchUtils
|
||||
import com.geode.launcher.utils.PreferenceUtils
|
||||
import java.io.File
|
||||
|
||||
private val DEFAULT_ROOT_PROJECTION: Array<String> = arrayOf(
|
||||
@@ -60,8 +63,17 @@ class UserDirectoryProvider : DocumentsProvider() {
|
||||
DocumentsContract.Root.FLAG_SUPPORTS_RECENTS or
|
||||
DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
|
||||
)
|
||||
add(DocumentsContract.Root.COLUMN_TITLE, context.getString(R.string.app_name))
|
||||
add(DocumentsContract.Root.COLUMN_ICON, R.drawable.geode_logo)
|
||||
|
||||
val selectedIconKey = PreferenceUtils.get(context).getString(PreferenceUtils.Key.SELECTED_ICON)
|
||||
?: "geode"
|
||||
val selectedIcon = ApplicationIcon.fromId(selectedIconKey)
|
||||
val iconDetails = IconUtils.getIconDetails(selectedIcon)
|
||||
|
||||
val nameId = if (selectedIcon == ApplicationIcon.SAPPHIRE) R.string.application_icon_sapphire else R.string.app_name
|
||||
val logoId = if (selectedIcon == ApplicationIcon.GEODE) R.drawable.geode_logo else iconDetails.iconId
|
||||
|
||||
add(DocumentsContract.Root.COLUMN_TITLE, context.getString(nameId))
|
||||
add(DocumentsContract.Root.COLUMN_ICON, logoId)
|
||||
}
|
||||
|
||||
return result
|
||||
|
||||
@@ -408,7 +408,8 @@ fun LauncherUpdateIndicator(modifier: Modifier = Modifier) {
|
||||
}
|
||||
}
|
||||
|
||||
ElevatedCard(modifier) {
|
||||
// there's like a single pixel of the shadow that gets clipped without this...
|
||||
ElevatedCard(modifier = modifier.padding(bottom = 1.dp)) {
|
||||
Column(
|
||||
// buttons add enough padding already, lower to compensate
|
||||
modifier = Modifier.padding(
|
||||
|
||||
@@ -169,12 +169,6 @@ fun DeveloperSettingsScreen(onBackPressedDispatcher: OnBackPressedDispatcher?) {
|
||||
}
|
||||
)
|
||||
|
||||
SettingsCard(
|
||||
title = stringResource(R.string.preference_native_pacing),
|
||||
description = stringResource(R.string.preference_native_pacing_description),
|
||||
preferenceKey = PreferenceUtils.Key.NATIVE_FRAME_PACING,
|
||||
)
|
||||
|
||||
/*
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
SettingsCard(
|
||||
|
||||
@@ -53,6 +53,7 @@ object GamePackageUtils {
|
||||
39L to "2.206",
|
||||
40L to "2.2074", // ok
|
||||
41L to "2.2081",
|
||||
42L to "2.209",
|
||||
)
|
||||
|
||||
/**
|
||||
|
||||
@@ -191,10 +191,16 @@ object GeodeUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun restartGame() {
|
||||
restartGame(false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun restartGame(safeMode: Boolean) {
|
||||
activity.get()?.run {
|
||||
packageManager.getLaunchIntentForPackage(packageName)?.also {
|
||||
val mainIntent = Intent.makeRestartActivityTask(it.component)
|
||||
mainIntent.putExtra("restarted", true)
|
||||
mainIntent.putExtra("safe-mode", safeMode)
|
||||
startActivity(mainIntent)
|
||||
exitProcess(0)
|
||||
}
|
||||
@@ -794,7 +800,7 @@ object GeodeUtils {
|
||||
|
||||
external fun internalKeyEvent(timestamp: Long, deviceId: Int, eventSource: Int, keyCode: Int, modifiers: Int, isDown: Boolean, repeatCount: Int)
|
||||
external fun internalTouchEvent(timestamp: Long, deviceId: Int, eventSource: Int, eventType: Int, ids: IntArray, xs: FloatArray, ys: FloatArray)
|
||||
external fun internalScrollEvent(timestamp: Long, deviceId: Int, eventSource: Int, scrollX: Float, scrollY: Float)
|
||||
external fun internalScrollEvent(timestamp: Long, deviceId: Int, eventSource: Int, scrollX: Float, scrollY: Float, mouseX: Float, mouseY: Float)
|
||||
external fun internalJoystickEvent(timestamp: Long, deviceId: Int, eventSource: Int, leftX: FloatArray, leftY: FloatArray, rightX: FloatArray, rightY: FloatArray, hatX: FloatArray, hatY: FloatArray, leftTrigger: FloatArray, rightTrigger: FloatArray)
|
||||
|
||||
external fun inputDeviceAdded(deviceId: Int)
|
||||
|
||||
@@ -44,6 +44,7 @@ enum class ApplicationIcon {
|
||||
GEODE,
|
||||
PRIDE,
|
||||
TRANS,
|
||||
GREEN,
|
||||
SAPPHIRE;
|
||||
|
||||
companion object {
|
||||
@@ -53,6 +54,7 @@ enum class ApplicationIcon {
|
||||
"pride" -> PRIDE
|
||||
"trans" -> TRANS
|
||||
"sapphire" -> SAPPHIRE
|
||||
"green" -> GREEN
|
||||
else -> DEFAULT
|
||||
}
|
||||
}
|
||||
@@ -63,6 +65,7 @@ enum class ApplicationIcon {
|
||||
DEFAULT -> "default"
|
||||
TRANS -> "trans"
|
||||
SAPPHIRE -> "sapphire"
|
||||
GREEN -> "green"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,5 +108,11 @@ object IconUtils {
|
||||
iconId = R.mipmap.ic_launcher_sapphire,
|
||||
nameId = R.string.application_icon_sapphire,
|
||||
)
|
||||
ApplicationIcon.GREEN -> ApplicationIconDetails(
|
||||
id = id.toId(),
|
||||
component = "MainActivityGreen",
|
||||
iconId = R.mipmap.ic_launcher_green,
|
||||
nameId = R.string.application_icon_green,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -150,7 +150,6 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
|
||||
LAST_LAUNCHER_UPDATE,
|
||||
DISABLE_UPDATE_CACHE,
|
||||
PREBUNDLED_GEODE_MIGRATION_PERFORMED,
|
||||
NATIVE_FRAME_PACING,
|
||||
}
|
||||
|
||||
private fun defaultValueForBooleanKey(key: Key): Boolean {
|
||||
@@ -202,7 +201,6 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
|
||||
Key.LAST_LAUNCHER_UPDATE -> "PreferenceLastLauncherUpdate"
|
||||
Key.DISABLE_UPDATE_CACHE -> "PreferenceDisableUpdateCache"
|
||||
Key.PREBUNDLED_GEODE_MIGRATION_PERFORMED -> "PreferencePrebundledGeodeMigrationPerformed"
|
||||
Key.NATIVE_FRAME_PACING -> "PreferenceNativeFramePacing"
|
||||
// Key.RELEASE_CHANNEL -> "PreferenceReleaseChannel"
|
||||
// Key.LAST_DISMISSED_UPDATE -> "PreferenceLastDismissedUpdate"
|
||||
// Key.FORCE_HRR -> "PreferenceForceHighRefreshRate"
|
||||
|
||||
@@ -8,32 +8,73 @@ import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Message
|
||||
import android.util.Log
|
||||
import android.text.InputType
|
||||
import android.view.InputDevice
|
||||
import android.view.KeyEvent
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
import android.view.WindowInsets
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.widget.AppCompatEditText
|
||||
import com.customRobTop.BaseRobTopActivity
|
||||
import com.geode.launcher.ExplicitGLSurfaceView
|
||||
import com.geode.launcher.LauncherFix
|
||||
import com.geode.launcher.utils.GeodeUtils
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.roundToLong
|
||||
|
||||
private const val HANDLER_OPEN_IME_KEYBOARD = 2
|
||||
private const val HANDLER_CLOSE_IME_KEYBOARD = 3
|
||||
private const val MS_TO_NS = 1_000_000
|
||||
private const val SEC_TO_NS = 1_000_000_000.0
|
||||
|
||||
class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
fun inputTypeForFlags(i: Int): Int {
|
||||
if ((i and BaseRobTopActivity.KEYBOARD_INPUT_NUMERIC) != 0) {
|
||||
return InputType.TYPE_NUMBER_FLAG_DECIMAL or InputType.TYPE_NUMBER_FLAG_SIGNED or InputType.TYPE_CLASS_NUMBER
|
||||
}
|
||||
|
||||
if ((i and BaseRobTopActivity.KEYBOARD_INPUT_PASSWORD) != 0) {
|
||||
return InputType.TYPE_TEXT_VARIATION_PASSWORD or InputType.TYPE_CLASS_TEXT
|
||||
}
|
||||
|
||||
if ((i and BaseRobTopActivity.KEYBOARD_INPUT_MULTILINE) != 0) {
|
||||
return InputType.TYPE_TEXT_FLAG_MULTI_LINE or InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or InputType.TYPE_CLASS_TEXT
|
||||
}
|
||||
|
||||
return InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS or InputType.TYPE_CLASS_TEXT
|
||||
}
|
||||
|
||||
fun usesInlineKeyboard(): Boolean {
|
||||
return Build.VERSION.SDK_INT >= 30
|
||||
}
|
||||
|
||||
fun imeOptionsForFlags(i: Int): Int {
|
||||
val i2 = if ((i and BaseRobTopActivity.KEYBOARD_INPUT_MULTILINE) == 0 || !usesInlineKeyboard())
|
||||
EditorInfo.IME_ACTION_DONE else EditorInfo.IME_ACTION_NONE
|
||||
|
||||
return if (usesInlineKeyboard())
|
||||
EditorInfo.IME_FLAG_NO_FULLSCREEN or EditorInfo.IME_FLAG_NO_EXTRACT_UI or i2
|
||||
else i2
|
||||
}
|
||||
|
||||
class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
||||
companion object {
|
||||
lateinit var cocos2dxGLSurfaceView: Cocos2dxGLSurfaceView
|
||||
private lateinit var handler: Handler
|
||||
private lateinit var cocos2dxTextInputWrapper: Cocos2dxTextInputWrapper
|
||||
|
||||
private var keyboardVisible = false
|
||||
private var keyboardHeight = 0
|
||||
|
||||
private var keyboardHideCheck = Runnable {
|
||||
if (keyboardVisible) {
|
||||
return@Runnable
|
||||
}
|
||||
|
||||
cocos2dxGLSurfaceView.queueEvent {
|
||||
cocos2dxGLSurfaceView.cocos2dxRenderer.handleTextClosed()
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun openIMEKeyboard() {
|
||||
val msg = Message()
|
||||
@@ -53,6 +94,30 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
cocos2dxGLSurfaceView.queueEvent {
|
||||
Cocos2dxAccelerometer.onSensorChanged(x, y, z, timestamp)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.R)
|
||||
fun onKeyboardInsets(windowInsets: WindowInsets) {
|
||||
if (!cocos2dxGLSurfaceView.sendNewCocosEvents) {
|
||||
return
|
||||
}
|
||||
|
||||
val visible = windowInsets.isVisible(WindowInsets.Type.ime())
|
||||
val offsetHeight = if (visible) windowInsets.getInsets(WindowInsets.Type.ime()).bottom else 0
|
||||
|
||||
if (visible == keyboardVisible && offsetHeight == keyboardHeight) {
|
||||
return
|
||||
}
|
||||
|
||||
keyboardVisible = visible
|
||||
keyboardHeight = offsetHeight
|
||||
|
||||
handler.removeCallbacks(keyboardHideCheck)
|
||||
if (!visible) {
|
||||
handler.postDelayed(keyboardHideCheck, 150L)
|
||||
} else {
|
||||
cocos2dxGLSurfaceView.cocos2dxRenderer.handleKeyboardWillShow(keyboardHeight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var cocos2dxRenderer: Cocos2dxRenderer
|
||||
@@ -67,7 +132,9 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
var controllerEventsEnabled = false
|
||||
private set
|
||||
var supportsResizeEvents = false
|
||||
var saveNativeFramePacing = false
|
||||
|
||||
// 2.209+ events for keyboard and such
|
||||
var sendNewCocosEvents = false
|
||||
|
||||
var cocos2dxEditText: AppCompatEditText? = null
|
||||
set(value) {
|
||||
@@ -89,9 +156,13 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
HANDLER_OPEN_IME_KEYBOARD -> {
|
||||
if (cocos2dxEditText?.requestFocus() == true) {
|
||||
cocos2dxEditText?.apply {
|
||||
removeTextChangedListener(
|
||||
cocos2dxTextInputWrapper
|
||||
)
|
||||
removeTextChangedListener(cocos2dxTextInputWrapper)
|
||||
|
||||
if (sendNewCocosEvents) {
|
||||
inputType = inputTypeForFlags(BaseRobTopActivity.curKeyboardInputFlags)
|
||||
imeOptions = imeOptionsForFlags(BaseRobTopActivity.curKeyboardInputFlags)
|
||||
}
|
||||
|
||||
setText("")
|
||||
val text = msg.obj as String
|
||||
append(text)
|
||||
@@ -104,16 +175,13 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
cocos2dxEditText,
|
||||
0
|
||||
)
|
||||
Log.d("GLSurfaceView", "showSoftInput")
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
HANDLER_CLOSE_IME_KEYBOARD -> {
|
||||
cocos2dxEditText?.apply {
|
||||
removeTextChangedListener(
|
||||
cocos2dxTextInputWrapper
|
||||
)
|
||||
removeTextChangedListener(cocos2dxTextInputWrapper)
|
||||
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(
|
||||
windowToken,
|
||||
0
|
||||
@@ -121,8 +189,10 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
}
|
||||
|
||||
requestFocus()
|
||||
Log.d("GLSurfaceView", "HideSoftInput")
|
||||
cocos2dxRenderer.handleTextClosed()
|
||||
|
||||
queueEvent {
|
||||
cocos2dxRenderer.handleTextClosed()
|
||||
}
|
||||
return
|
||||
}
|
||||
else -> return
|
||||
@@ -143,10 +213,6 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
|
||||
println("updateRefreshRate: selecting refresh rate of ${chosenDisplay.refreshRate} (display ${chosenDisplay.modeId})")
|
||||
|
||||
if (saveNativeFramePacing) {
|
||||
LauncherFix.setSwapInterval((1_000_000_000L / chosenRefreshRate).roundToLong())
|
||||
}
|
||||
|
||||
holder.surface.setFrameRate(chosenRefreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
|
||||
if (isAttachedToWindow) {
|
||||
(context as Activity).window.attributes.preferredRefreshRate = chosenRefreshRate
|
||||
@@ -161,7 +227,6 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
|
||||
renderMode = RENDERMODE_CONTINUOUSLY
|
||||
queueEvent { cocos2dxRenderer.handleOnResume() }
|
||||
}
|
||||
@@ -306,6 +371,97 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun queueKeyboardEvent(keyCode: Int, event: KeyEvent, down: Boolean): Boolean {
|
||||
val keypadCode = keyCode == KeyEvent.KEYCODE_MENU || keyCode == KeyEvent.KEYCODE_BACK
|
||||
if (event.isSystem && !keypadCode) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (keypadCode && BaseRobTopActivity.blockBackButton) {
|
||||
return true
|
||||
}
|
||||
|
||||
queueEvent {
|
||||
sendNextEventTimestamp(event.eventTime * MS_TO_NS)
|
||||
}
|
||||
|
||||
if (useKeyboardEvents) {
|
||||
if (down) {
|
||||
queueEvent {
|
||||
GeodeUtils.nativeKeyDown(keyCode, event.modifiers, event.repeatCount != 0)
|
||||
}
|
||||
} else {
|
||||
queueEvent {
|
||||
GeodeUtils.nativeKeyUp(keyCode, event.modifiers)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (controllerEventsEnabled) {
|
||||
queueEvent {
|
||||
GeodeUtils.internalKeyEvent(
|
||||
event.eventTime * MS_TO_NS,
|
||||
event.deviceId,
|
||||
event.source,
|
||||
event.keyCode,
|
||||
event.modifiers,
|
||||
down,
|
||||
event.repeatCount
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// geode events override native callbacks
|
||||
if (useKeyboardEvents || controllerEventsEnabled) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (sendNewCocosEvents) {
|
||||
if (KeyEvent.isGamepadButton(keyCode)) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (keyCode == KeyEvent.KEYCODE_ESCAPE && BaseRobTopActivity.blockBackButton) {
|
||||
return true
|
||||
}
|
||||
|
||||
// messy logic
|
||||
val isRepeat = event.repeatCount > 0
|
||||
if (keypadCode) {
|
||||
if (down) {
|
||||
if (isRepeat || BaseRobTopActivity.blockBackButton) {
|
||||
return true
|
||||
}
|
||||
|
||||
queueEvent {
|
||||
cocos2dxRenderer.handleKeyDown(keyCode)
|
||||
}
|
||||
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
val eventTimestamp = event.eventTime / 1000.0
|
||||
|
||||
val shiftPressed = event.isShiftPressed
|
||||
val ctrlPressed = event.isCtrlPressed
|
||||
val altPressed = event.isAltPressed
|
||||
|
||||
queueEvent {
|
||||
cocos2dxRenderer.handleKeyboardEvent(
|
||||
keyCode, down, isRepeat, eventTimestamp,
|
||||
shiftPressed, ctrlPressed, altPressed
|
||||
)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
private fun legacyKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean {
|
||||
return when (keyCode) {
|
||||
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_MENU -> {
|
||||
@@ -363,65 +519,19 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
}
|
||||
|
||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (!useKeyboardEvents && !controllerEventsEnabled) {
|
||||
if (!queueKeyboardEvent(keyCode, event, true)) {
|
||||
return legacyKeyDown(keyCode, event)
|
||||
}
|
||||
|
||||
return when (keyCode) {
|
||||
// ignore system keys
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_MUTE -> {
|
||||
super.onKeyDown(keyCode, event)
|
||||
}
|
||||
else -> {
|
||||
if (BaseRobTopActivity.blockBackButton) {
|
||||
return true
|
||||
}
|
||||
|
||||
queueEvent {
|
||||
sendNextEventTimestamp(event.eventTime * MS_TO_NS)
|
||||
|
||||
if (useKeyboardEvents) {
|
||||
GeodeUtils.nativeKeyDown(keyCode, event.modifiers, event.repeatCount != 0)
|
||||
}
|
||||
|
||||
if (controllerEventsEnabled) {
|
||||
GeodeUtils.internalKeyEvent(event.eventTime * MS_TO_NS, event.deviceId, event.source, event.keyCode, event.modifiers, true, event.repeatCount)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
|
||||
if (!useKeyboardEvents && !controllerEventsEnabled) {
|
||||
if (!queueKeyboardEvent(keyCode, event, false)) {
|
||||
return super.onKeyUp(keyCode, event)
|
||||
}
|
||||
|
||||
return when (keyCode) {
|
||||
KeyEvent.KEYCODE_VOLUME_DOWN, KeyEvent.KEYCODE_VOLUME_UP, KeyEvent.KEYCODE_MUTE -> {
|
||||
super.onKeyUp(keyCode, event)
|
||||
}
|
||||
else -> {
|
||||
if (event.repeatCount != 0 || BaseRobTopActivity.blockBackButton) {
|
||||
return true
|
||||
}
|
||||
|
||||
queueEvent {
|
||||
sendNextEventTimestamp(event.eventTime * MS_TO_NS)
|
||||
|
||||
if (useKeyboardEvents) {
|
||||
GeodeUtils.nativeKeyUp(keyCode, event.modifiers)
|
||||
}
|
||||
|
||||
if (controllerEventsEnabled) {
|
||||
GeodeUtils.internalKeyEvent(event.eventTime * MS_TO_NS, event.deviceId, event.source, event.keyCode, event.modifiers, false, event.repeatCount)
|
||||
}
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// i'm lazy.
|
||||
@@ -484,11 +594,15 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
}
|
||||
|
||||
override fun onGenericMotionEvent(event: MotionEvent?): Boolean {
|
||||
if (!useKeyboardEvents && !controllerEventsEnabled) {
|
||||
val hasGeodeEvents = useKeyboardEvents || controllerEventsEnabled
|
||||
if (!hasGeodeEvents && !sendNewCocosEvents) {
|
||||
return super.onGenericMotionEvent(event)
|
||||
}
|
||||
|
||||
if (event?.action == MotionEvent.ACTION_SCROLL) {
|
||||
val mouseX = event.x
|
||||
val mouseY = event.y
|
||||
|
||||
val scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL)
|
||||
val scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
|
||||
val timestamp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
|
||||
@@ -502,7 +616,11 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
}
|
||||
|
||||
if (controllerEventsEnabled) {
|
||||
GeodeUtils.internalScrollEvent(timestamp, event.deviceId, event.source, scrollX, scrollY)
|
||||
GeodeUtils.internalScrollEvent(timestamp, event.deviceId, event.source, scrollX, scrollY, mouseX, mouseY)
|
||||
}
|
||||
|
||||
if (!hasGeodeEvents && sendNewCocosEvents) {
|
||||
cocos2dxRenderer.handleScroll(mouseX, mouseY, scrollY, scrollX)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -542,11 +660,6 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
setRenderer(this.cocos2dxRenderer)
|
||||
}
|
||||
|
||||
override fun setNativeFramePacing(nativeFramePacing: Boolean) {
|
||||
super.setNativeFramePacing(nativeFramePacing)
|
||||
saveNativeFramePacing = nativeFramePacing
|
||||
}
|
||||
|
||||
private fun getContentText(): String {
|
||||
return cocos2dxRenderer.getContentText()
|
||||
}
|
||||
@@ -581,10 +694,6 @@ class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
|
||||
inputManager.unregisterInputDeviceListener(InputListener)
|
||||
}
|
||||
|
||||
fun setMaxFrameRate(rate: Int) {
|
||||
cocos2dxRenderer.limitFrameRate(rate)
|
||||
}
|
||||
|
||||
object InputListener : InputManager.InputDeviceListener {
|
||||
override fun onInputDeviceAdded(deviceId: Int) {
|
||||
GeodeUtils.inputDeviceAdded(deviceId)
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
package org.cocos2dx.lib
|
||||
|
||||
import android.opengl.EGLConfig
|
||||
import android.opengl.GLSurfaceView
|
||||
import android.os.Build
|
||||
import com.geode.launcher.ExplicitGLSurfaceView
|
||||
import javax.microedition.khronos.egl.EGLConfig
|
||||
import javax.microedition.khronos.opengles.GL10
|
||||
|
||||
private const val NANOSECONDS_PER_SECOND = 1000000000L
|
||||
private const val NANOSECONDS_PER_MICROSECOND = 1000000L
|
||||
|
||||
@Suppress("unused", "KotlinJniMissingFunction")
|
||||
class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : ExplicitGLSurfaceView.Renderer {
|
||||
class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceView.Renderer {
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun setAnimationInterval(@Suppress("UNUSED_PARAMETER") animationInterval: Double) {
|
||||
@@ -27,12 +28,29 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : ExplicitGLS
|
||||
@JvmStatic
|
||||
private external fun nativeKeyDown(keyCode: Int): Boolean
|
||||
|
||||
@JvmStatic
|
||||
private external fun nativeKeyboardEvent(
|
||||
keyCode: Int,
|
||||
down: Boolean,
|
||||
isRepeat: Boolean,
|
||||
timestamp: Double,
|
||||
shiftPressed: Boolean,
|
||||
ctrlPressed: Boolean,
|
||||
altPressed: Boolean
|
||||
)
|
||||
|
||||
@JvmStatic
|
||||
private external fun nativeKeyboardWillShow(offset: Int)
|
||||
|
||||
@JvmStatic
|
||||
private external fun nativeOnPause()
|
||||
|
||||
@JvmStatic
|
||||
private external fun nativeOnResume()
|
||||
|
||||
@JvmStatic
|
||||
private external fun nativeScroll(x: Float, y: Float, scrollY: Float, scrollX: Float)
|
||||
|
||||
@JvmStatic
|
||||
private external fun nativeRender()
|
||||
|
||||
@@ -79,12 +97,12 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : ExplicitGLS
|
||||
screenHeight = surfaceHeight
|
||||
}
|
||||
|
||||
override fun onSurfaceCreated(eglConfig: EGLConfig?) {
|
||||
override fun onSurfaceCreated(gl10: GL10?, eglConfig: EGLConfig?) {
|
||||
nativeInit(screenWidth, screenHeight)
|
||||
lastTickInNanoSeconds = System.nanoTime()
|
||||
}
|
||||
|
||||
override fun onSurfaceChanged(width: Int, height: Int) {
|
||||
override fun onSurfaceChanged(gl10: GL10?, width: Int, height: Int) {
|
||||
if (setFrameRate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
handler.updateRefreshRate()
|
||||
}
|
||||
@@ -92,7 +110,7 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : ExplicitGLS
|
||||
println("renderer surfaceChanged: ${width}x${height}@${handler.display.refreshRate}fps")
|
||||
}
|
||||
|
||||
override fun onDrawFrame() {
|
||||
override fun onDrawFrame(gl: GL10?) {
|
||||
val animationInterval = mAnimationInterval
|
||||
if (animationInterval == null) {
|
||||
nativeRender()
|
||||
@@ -132,6 +150,18 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : ExplicitGLS
|
||||
nativeKeyDown(keyCode)
|
||||
}
|
||||
|
||||
fun handleKeyboardWillShow(offset: Int) {
|
||||
nativeKeyboardWillShow(offset)
|
||||
}
|
||||
|
||||
fun handleKeyboardEvent(keyCode: Int, down: Boolean, isRepeat: Boolean, timestamp: Double, shiftPressed: Boolean, ctrlPressed: Boolean, altPressed: Boolean) {
|
||||
nativeKeyboardEvent(keyCode, down, isRepeat, timestamp, shiftPressed, ctrlPressed, altPressed)
|
||||
}
|
||||
|
||||
fun handleScroll(x: Float, y: Float, scrollY: Float, scrollX: Float) {
|
||||
nativeScroll(x, y, scrollY, scrollX)
|
||||
}
|
||||
|
||||
fun handleOnPause() {
|
||||
nativeOnPause()
|
||||
}
|
||||
|
||||
@@ -5,8 +5,10 @@ import android.text.TextWatcher
|
||||
import android.widget.TextView.OnEditorActionListener
|
||||
import android.text.Editable
|
||||
import android.view.KeyEvent
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import com.customRobTop.JniToCpp
|
||||
|
||||
class Cocos2dxTextInputWrapper(private val cocos2dxGLSurfaceView: Cocos2dxGLSurfaceView) : TextWatcher, OnEditorActionListener {
|
||||
private var originText: String? = null
|
||||
@@ -19,21 +21,33 @@ class Cocos2dxTextInputWrapper(private val cocos2dxGLSurfaceView: Cocos2dxGLSurf
|
||||
this.originText = originText
|
||||
}
|
||||
|
||||
override fun afterTextChanged(s: Editable) {
|
||||
if (!isFullScreenEdit) {
|
||||
var nModified = s.length - text!!.length
|
||||
if (nModified > 0) {
|
||||
cocos2dxGLSurfaceView.insertText(
|
||||
s.subSequence(text!!.length, s.length).toString()
|
||||
)
|
||||
} else {
|
||||
while (nModified < 0) {
|
||||
cocos2dxGLSurfaceView.deleteBackward()
|
||||
nModified++
|
||||
}
|
||||
}
|
||||
text = s.toString()
|
||||
override fun afterTextChanged(editable: Editable) {
|
||||
if (isFullScreenEdit) {
|
||||
return
|
||||
}
|
||||
|
||||
val cText = text ?: return
|
||||
|
||||
val str = editable.toString()
|
||||
val unchanged = str.commonPrefixWith(cText)
|
||||
|
||||
var unchangedUntil = unchanged.length
|
||||
if (unchangedUntil > 0 && unchangedUntil < str.length && Character.isLowSurrogate(str[unchangedUntil])) {
|
||||
unchangedUntil--
|
||||
}
|
||||
|
||||
val codePointLength = cText.codePointCount(unchangedUntil, cText.length)
|
||||
repeat(codePointLength) {
|
||||
cocos2dxGLSurfaceView.deleteBackward()
|
||||
}
|
||||
|
||||
if (unchangedUntil < str.length) {
|
||||
cocos2dxGLSurfaceView.insertText(
|
||||
str.substring(unchangedUntil)
|
||||
)
|
||||
}
|
||||
|
||||
text = str
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(
|
||||
@@ -47,22 +61,45 @@ class Cocos2dxTextInputWrapper(private val cocos2dxGLSurfaceView: Cocos2dxGLSurf
|
||||
|
||||
override fun onTextChanged(pCharSequence: CharSequence, start: Int, before: Int, count: Int) {}
|
||||
override fun onEditorAction(textView: TextView, actionID: Int, keyEvent: KeyEvent?): Boolean {
|
||||
if (cocos2dxGLSurfaceView.cocos2dxEditText == textView && isFullScreenEdit) {
|
||||
for (i in originText!!.length downTo 1) {
|
||||
cocos2dxGLSurfaceView.deleteBackward()
|
||||
}
|
||||
val fullscreenIme = cocos2dxGLSurfaceView.cocos2dxEditText == textView && isFullScreenEdit
|
||||
if (fullscreenIme) {
|
||||
var text = textView.text.toString()
|
||||
if (!text.endsWith('\n')) {
|
||||
text += '\n'
|
||||
}
|
||||
if (originText != text || !cocos2dxGLSurfaceView.sendNewCocosEvents) {
|
||||
if (cocos2dxGLSurfaceView.sendNewCocosEvents) {
|
||||
cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.textInputCaretToEnd()
|
||||
}
|
||||
}
|
||||
|
||||
cocos2dxGLSurfaceView.insertText(text)
|
||||
repeat(originText!!.length) {
|
||||
cocos2dxGLSurfaceView.deleteBackward()
|
||||
}
|
||||
|
||||
if (!cocos2dxGLSurfaceView.sendNewCocosEvents) {
|
||||
if (!text.endsWith('\n')) {
|
||||
text += '\n'
|
||||
}
|
||||
}
|
||||
|
||||
if (text.isNotEmpty()) {
|
||||
cocos2dxGLSurfaceView.insertText(text)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (actionID != 6) {
|
||||
return false
|
||||
|
||||
if (fullscreenIme || actionID == EditorInfo.IME_ACTION_DONE) {
|
||||
if (cocos2dxGLSurfaceView.sendNewCocosEvents) {
|
||||
cocos2dxGLSurfaceView.queueEvent {
|
||||
JniToCpp.textInputDone()
|
||||
}
|
||||
}
|
||||
}
|
||||
cocos2dxGLSurfaceView.requestFocus()
|
||||
Cocos2dxGLSurfaceView.closeIMEKeyboard()
|
||||
|
||||
if (actionID == EditorInfo.IME_ACTION_DONE) {
|
||||
cocos2dxGLSurfaceView.requestFocus()
|
||||
Cocos2dxGLSurfaceView.closeIMEKeyboard()
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,28 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<group android:scaleX="0.75" android:scaleY="0.75" android:translateX="128" android:translateY="128">
|
||||
android:viewportWidth="1366"
|
||||
android:viewportHeight="1366">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h1024v1024h-1024z"/>
|
||||
android:pathData="M0,0h1366v1366h-1366z"/>
|
||||
<path
|
||||
android:pathData="M0,0h1024v1024h-1024z"
|
||||
android:pathData="M0,0h1366v1366h-1366z"
|
||||
android:fillColor="#F4D48E"/>
|
||||
<path
|
||||
android:pathData="M512,512m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
|
||||
android:pathData="M598,683m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
|
||||
android:fillColor="#F4D48E"/>
|
||||
<path
|
||||
android:pathData="M719.5,289C618,241.7 526,152.5 320.5,152.5C115,152.5 3.5,248 -8.5,293C-20.5,338 -8.2,391.5 -13.5,390.5L-33,706.5H1145L1137.5,460C1115.5,426.8 1043.5,399 1007,391.5C970.5,384 822.5,337 719.5,289Z"
|
||||
android:pathData="M873.8,459.7C752.5,412.3 642.7,323 397.2,323C151.8,323 18.6,418.7 4.3,463.8C-10.1,508.8 4.7,562.4 -1.7,561.4L-25,878H1382L1373,631.1C1346.8,597.8 1260.8,569.9 1217.2,562.4C1173.6,554.9 996.8,507.8 873.8,459.7Z"
|
||||
android:fillColor="#F4A47B"/>
|
||||
<path
|
||||
android:pathData="M561.5,514.5C488.5,476.5 341,402 214.5,402C88,402 30.3,440.7 -8.5,461.5C-47.3,482.3 -54,485.2 -59,486.5L-68.5,808L563.5,814.5L561.5,514.5Z"
|
||||
android:pathData="M647.9,685.4C569.9,647.4 412.4,573 277.3,573C142.1,573 80.5,611.7 39.1,632.4C-2.4,653.2 -9.5,656.1 -14.9,657.4L-25,978.5L650,985L647.9,685.4Z"
|
||||
android:fillColor="#E27880"/>
|
||||
<path
|
||||
android:pathData="M1020.5,551C994,521.2 893.7,454.1 662.5,452.5C539.2,451.6 363,505.6 259,577.5C163.9,643.2 43,680 -13,686L-124,934.5H1187.5L1020.5,551Z"
|
||||
android:pathData="M1362.1,728.7C1327,696.7 1194.3,624.7 888.5,623C725.4,622.1 492.3,679.9 354.7,757.1C228.9,827.6 68.9,867 -5.2,873.5L-152,1140H1583L1362.1,728.7Z"
|
||||
android:fillColor="#B9588F"/>
|
||||
<path
|
||||
android:pathData="M819.5,856C638,856 518,772.5 359,772.5C200,772.5 100,860.5 65,869L-94,949.5V1041.5L1177.5,1025.5L1032.5,738C1028,758 967.4,856 819.5,856Z"
|
||||
android:pathData="M1107,1027.2C885.5,1027.2 739,943.6 544.9,943.6C350.9,943.6 228.8,1031.7 186.1,1040.2L-8,1120.8V1385L1376,1379L1367,909C1361.5,929 1287.6,1027.2 1107,1027.2Z"
|
||||
android:fillColor="#714A9A"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,29 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1366"
|
||||
android:viewportHeight="1366">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h1366v1366h-1366z"/>
|
||||
<path
|
||||
android:pathData="M0,0h1366v1366h-1366z"
|
||||
android:fillColor="#9EF4C3"/>
|
||||
<path
|
||||
android:pathData="M598,683m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
|
||||
android:fillColor="#9EF4C3"/>
|
||||
<path
|
||||
android:pathData="M873.8,459.7C752.5,412.3 642.7,323 397.2,323C151.8,323 18.6,418.7 4.3,463.8C-10.1,508.8 4.7,562.4 -1.7,561.4L-25,878H1382L1373,631.1C1346.8,597.8 1260.8,569.9 1217.2,562.4C1173.6,554.9 996.8,507.8 873.8,459.7Z"
|
||||
android:fillColor="#6DDBAD"/>
|
||||
<path
|
||||
android:pathData="M647.9,685.4C569.9,647.4 412.4,573 277.3,573C142.1,573 80.5,611.7 39.1,632.4C-2.4,653.2 -9.5,656.1 -14.9,657.4L-25,978.5L650,985L647.9,685.4Z"
|
||||
android:fillColor="#64C780"/>
|
||||
<path
|
||||
android:pathData="M1362.1,728.7C1327,696.7 1194.3,624.7 888.5,623C725.4,622.1 492.3,679.9 354.7,757.1C228.9,827.6 68.9,867 -5.2,873.5L-152,1140H1583L1362.1,728.7Z"
|
||||
android:fillColor="#46A47D"/>
|
||||
<path
|
||||
android:pathData="M1107,1027.2C885.5,1027.2 739,943.6 544.9,943.6C350.9,943.6 228.8,1031.7 186.1,1040.2L-8,1120.8V1385L1376,1379L1367,909C1361.5,929 1287.6,1027.2 1107,1027.2Z"
|
||||
android:fillColor="#417563"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<group android:scaleX="0.75" android:scaleY="0.75" android:translateX="128" android:translateY="128">
|
||||
android:viewportWidth="1366"
|
||||
android:viewportHeight="1366">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h1024v1024h-1024z"/>
|
||||
android:pathData="M0,0h1366v1366h-1366z"/>
|
||||
<path
|
||||
android:pathData="M0,0h1024v1024h-1024z"
|
||||
android:pathData="M0,0h1366v1366h-1366z"
|
||||
android:fillColor="#CC7C7C"/>
|
||||
<path
|
||||
android:pathData="M512,512m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
|
||||
android:pathData="M598,683m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
|
||||
android:fillColor="#CC7C7C"/>
|
||||
<path
|
||||
android:pathData="M719.5,289C618,241.7 526,152.5 320.5,152.5C115,152.5 3.5,248 -8.5,293C-20.5,338 -8.2,391.5 -13.5,390.5L-33,706.5H1145L1137.5,460C1115.5,426.8 1043.5,399 1007,391.5C970.5,384 822.5,337 719.5,289Z"
|
||||
android:pathData="M873.8,459.7C752.5,412.3 642.7,323 397.2,323C151.8,323 18.6,418.7 4.3,463.8C-10.1,508.8 4.7,562.4 -1.7,561.4L-25,878H1382L1373,631.1C1346.8,597.8 1260.8,569.9 1217.2,562.4C1173.6,554.9 996.8,507.8 873.8,459.7Z"
|
||||
android:fillColor="#C6B471"/>
|
||||
<path
|
||||
android:pathData="M561.5,514.5C488.5,476.5 341,402 214.5,402C88,402 30.3,440.7 -8.5,461.5C-47.3,482.3 -54,485.2 -59,486.5L-68.5,808L563.5,814.5L561.5,514.5Z"
|
||||
android:pathData="M647.9,685.4C569.9,647.4 412.4,573 277.3,573C142.1,573 80.5,611.7 39.1,632.4C-2.4,653.2 -9.5,656.1 -14.9,657.4L-25,978.5L650,985L647.9,685.4Z"
|
||||
android:fillColor="#59AF7F"/>
|
||||
<path
|
||||
android:pathData="M1020.5,551C994,521.2 893.7,454.1 662.5,452.5C539.2,451.6 363,505.6 259,577.5C163.9,643.2 43,680 -13,686L-124,934.5H1187.5L1020.5,551Z"
|
||||
android:pathData="M1362.1,728.7C1327,696.7 1194.3,624.7 888.5,623C725.4,622.1 492.3,679.9 354.7,757.1C228.9,827.6 68.9,867 -5.2,873.5L-152,1140H1583L1362.1,728.7Z"
|
||||
android:fillColor="#5F8CAB"/>
|
||||
<path
|
||||
android:pathData="M819.5,856C638,856 518,772.5 359,772.5C200,772.5 100,860.5 65,869L-94,949.5V1041.5L1177.5,1025.5L1032.5,738C1028,758 967.4,856 819.5,856Z"
|
||||
android:pathData="M1107,1027.2C885.5,1027.2 739,943.6 544.9,943.6C350.9,943.6 228.8,1031.7 186.1,1040.2L-8,1120.8V1385L1376,1379L1367,909C1361.5,929 1287.6,1027.2 1107,1027.2Z"
|
||||
android:fillColor="#70558D"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@@ -1,28 +1,29 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="108dp"
|
||||
android:height="108dp"
|
||||
android:viewportWidth="1024"
|
||||
android:viewportHeight="1024">
|
||||
<group android:scaleX="0.75" android:scaleY="0.75" android:translateX="128" android:translateY="128">
|
||||
android:viewportWidth="1366"
|
||||
android:viewportHeight="1366">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h1024v1024h-1024z"/>
|
||||
android:pathData="M0,0h1366v1366h-1366z"/>
|
||||
<path
|
||||
android:pathData="M0,0h1024v1024h-1024z"
|
||||
android:pathData="M0,0h1366v1366h-1366z"
|
||||
android:fillColor="#65B8C2"/>
|
||||
<path
|
||||
android:pathData="M512,512m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
|
||||
android:pathData="M598,683m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
|
||||
android:fillColor="#65B8C2"/>
|
||||
<path
|
||||
android:pathData="M719.5,289C618,241.7 526,152.5 320.5,152.5C115,152.5 3.5,248 -8.5,293C-20.5,338 -8.2,391.5 -13.5,390.5L-33,706.5H1145L1137.5,460C1115.5,426.8 1043.5,399 1007,391.5C970.5,384 822.5,337 719.5,289Z"
|
||||
android:pathData="M873.8,459.7C752.5,412.3 642.7,323 397.2,323C151.8,323 18.6,418.7 4.3,463.8C-10.1,508.8 4.7,562.4 -1.7,561.4L-25,878H1382L1373,631.1C1346.8,597.8 1260.8,569.9 1217.2,562.4C1173.6,554.9 996.8,507.8 873.8,459.7Z"
|
||||
android:fillColor="#DEA5CD"/>
|
||||
<path
|
||||
android:pathData="M561.5,514.5C488.5,476.5 341,402 214.5,402C88,402 30.3,440.7 -8.5,461.5C-47.3,482.3 -54,485.2 -59,486.5L-68.5,808L563.5,814.5L561.5,514.5Z"
|
||||
android:pathData="M647.9,685.4C569.9,647.4 412.4,573 277.3,573C142.1,573 80.5,611.7 39.1,632.4C-2.4,653.2 -9.5,656.1 -14.9,657.4L-25,978.5L650,985L647.9,685.4Z"
|
||||
android:fillColor="#DEE8EA"/>
|
||||
<path
|
||||
android:pathData="M1020.5,551C994,521.2 893.7,454.1 662.5,452.5C539.2,451.6 363,505.6 259,577.5C163.9,643.2 43,680 -13,686L-124,934.5H1187.5L1020.5,551Z"
|
||||
android:pathData="M1362.1,728.7C1327,696.7 1194.3,624.7 888.5,623C725.4,622.1 492.3,679.9 354.7,757.1C228.9,827.6 68.9,867 -5.2,873.5L-152,1140H1583L1362.1,728.7Z"
|
||||
android:fillColor="#C485BD"/>
|
||||
<path
|
||||
android:pathData="M819.5,856C638,856 518,772.5 359,772.5C200,772.5 100,860.5 65,869L-94,949.5V1041.5L1177.5,1025.5L1032.5,738C1028,758 967.4,856 819.5,856Z"
|
||||
android:pathData="M1107,1027.2C885.5,1027.2 739,943.6 544.9,943.6C350.9,943.6 228.8,1031.7 186.1,1040.2L-8,1120.8V1385L1376,1379L1367,909C1361.5,929 1287.6,1027.2 1107,1027.2Z"
|
||||
android:fillColor="#259CB4"/>
|
||||
</group>
|
||||
</vector>
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_green_background"/>
|
||||
<foreground android:drawable="@drawable/ic_launcher_monochrome_shadow"/>
|
||||
</adaptive-icon>
|
||||
@@ -298,9 +298,6 @@
|
||||
<string name="preference_profiles_delete_alt">Delete</string>
|
||||
<string name="preference_profiles_delete_undo_alt">Undo</string>
|
||||
|
||||
<string name="preference_native_pacing">Native frame pacing</string>
|
||||
<string name="preference_native_pacing_description">Reduces latency, but may worsen frame drops.</string>
|
||||
|
||||
<!-- ApplicationLogsActivity strings -->
|
||||
<string name="title_activity_application_logs">Application logs</string>
|
||||
<string name="application_logs_no_logs">No logs available.</string>
|
||||
@@ -341,6 +338,7 @@
|
||||
<string name="application_icon_pride">Pride</string>
|
||||
<string name="application_icon_trans">Transgender</string>
|
||||
<string name="application_icon_sapphire">Sapphire</string>
|
||||
<string name="application_icon_green">Android</string>
|
||||
<string name="application_icon_confirm">Change icon to %1$s?</string>
|
||||
<string name="application_icon_confirm_description">Geode Launcher will close itself to change the application icon.</string>
|
||||
|
||||
|
||||
+4
-4
@@ -1,9 +1,9 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "9.4.0" apply false
|
||||
id("com.android.library") version "9.4.0" apply false
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.4.10" apply false
|
||||
id("org.jetbrains.kotlin.plugin.compose") version "2.4.10" apply false
|
||||
id("com.android.application") version "9.4.1" apply false
|
||||
id("com.android.library") version "9.4.1" apply false
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.4.21" apply false
|
||||
id("org.jetbrains.kotlin.plugin.compose") version "2.4.21" apply false
|
||||
}
|
||||
|
||||
tasks.register<Delete>("clean") {
|
||||
|
||||
Reference in new issue
Block a user