Compare commits

..
7 Commits
Author SHA1 Message Date
Chloe df513983d1 cover the license 2026-09-07 17:45:15 -07:00
Chloe a71071e3ac Merge branch 'main' into latency-test 2026-09-07 17:22:30 -07:00
Chloe 8578200db1 Merge branch 'main' into latency-test 2026-09-07 00:52:32 -07:00
Chloe adb9dcc1e9 add toggle for frame pacing 2026-09-07 00:41:18 -07:00
Chloe a32589b06c disable auto swap interval 2026-09-07 00:41:18 -07:00
Chloe 1773e54c84 fix surface update being called outside gl thread 2026-09-07 00:41:18 -07:00
Chloe 5718b73470 my evil test 2026-09-07 00:41:17 -07:00
29 changed files with 1895 additions and 810 deletions

No files matched your search

+12 -9
View File
@@ -1,4 +1,5 @@
import groovy.json.JsonSlurper import groovy.json.JsonSlurper
import java.io.IOException
import java.net.URI import java.net.URI
import java.security.MessageDigest import java.security.MessageDigest
import java.util.zip.ZipInputStream import java.util.zip.ZipInputStream
@@ -15,7 +16,7 @@ plugins {
id("org.jetbrains.kotlin.plugin.compose") id("org.jetbrains.kotlin.plugin.compose")
} }
val currentGeodeVersion = "5.11.2" val currentGeodeVersion = "5.10.1"
abstract class DownloadVersionMetadataTask : DefaultTask() { abstract class DownloadVersionMetadataTask : DefaultTask() {
@get:Input @get:Input
@@ -247,9 +248,9 @@ android {
defaultConfig { defaultConfig {
applicationId = "com.geode.launcher" applicationId = "com.geode.launcher"
minSdk = 23 minSdk = 23
targetSdk = 37 targetSdk = 36
versionCode = 31 versionCode = 30
versionName = "1.9.0" versionName = "1.8.0"
vectorDrawables { vectorDrawables {
useSupportLibrary = true useSupportLibrary = true
@@ -317,6 +318,7 @@ android {
buildFeatures { buildFeatures {
compose = true compose = true
buildConfig = true buildConfig = true
prefab = true
} }
packaging { packaging {
resources { resources {
@@ -330,7 +332,7 @@ android {
} }
} }
namespace = "com.geode.launcher" namespace = "com.geode.launcher"
ndkVersion = "30.0.16248370" ndkVersion = "29.0.14206865"
} }
androidComponents { androidComponents {
@@ -347,8 +349,8 @@ androidComponents {
} }
dependencies { dependencies {
implementation (platform("androidx.compose:compose-bom:2026.09.00")) implementation (platform("androidx.compose:compose-bom:2026.08.00"))
implementation ("androidx.core:core-ktx:1.19.1") implementation ("androidx.core:core-ktx:1.19.0")
implementation ("androidx.compose.ui:ui") implementation ("androidx.compose.ui:ui")
implementation ("androidx.compose.material3:material3") implementation ("androidx.compose.material3:material3")
implementation ("androidx.compose.ui:ui-tooling-preview") implementation ("androidx.compose.ui:ui-tooling-preview")
@@ -358,6 +360,7 @@ dependencies {
implementation ("androidx.activity:activity-ktx:1.13.0") implementation ("androidx.activity:activity-ktx:1.13.0")
implementation ("androidx.appcompat:appcompat:1.8.0") implementation ("androidx.appcompat:appcompat:1.8.0")
implementation ("androidx.documentfile:documentfile:1.1.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.okio:okio:3.18.2")
implementation ("com.squareup.okhttp3:okhttp:5.5.0") implementation ("com.squareup.okhttp3:okhttp:5.5.0")
@@ -367,8 +370,8 @@ dependencies {
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.11.0") implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.11.0")
implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0") implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.8.0")
implementation ("com.mikepenz:multiplatform-markdown-renderer-android:0.46.0") implementation ("com.mikepenz:multiplatform-markdown-renderer-android:0.45.0")
implementation ("com.mikepenz:multiplatform-markdown-renderer-m3:0.46.0") implementation ("com.mikepenz:multiplatform-markdown-renderer-m3:0.45.0")
implementation ("androidx.browser:browser:1.10.0") implementation ("androidx.browser:browser:1.10.0")
debugImplementation ("androidx.compose.ui:ui-tooling") debugImplementation ("androidx.compose.ui:ui-tooling")
coreLibraryDesugaring ("com.android.tools:desugar_jdk_libs:2.1.5") coreLibraryDesugaring ("com.android.tools:desugar_jdk_libs:2.1.5")
+1 -21
View File
@@ -125,7 +125,7 @@
</activity> </activity>
<activity <activity
android:name=".GeometryDashActivity" android:name=".GeometryDashActivity"
android:configChanges="fontScale|layoutDirection|density|smallestScreenSize|screenSize|uiMode|screenLayout|orientation|navigation|keyboardHidden|keyboard|locale" android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout|keyboard"
android:exported="true" android:exported="true"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:screenOrientation="landscape" android:screenOrientation="landscape"
@@ -233,26 +233,6 @@
<data android:scheme="geode-launcher" android:host="main" /> <data android:scheme="geode-launcher" android:host="main" />
</intent-filter> </intent-filter>
</activity-alias> </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 <provider
android:name=".UserDirectoryProvider" android:name=".UserDirectoryProvider"
+5 -2
View File
@@ -2,7 +2,9 @@ cmake_minimum_required(VERSION 3.21)
project(launcherfix) project(launcherfix)
add_library(launcherfix SHARED launcher-fix.cpp patcher.cpp) find_package(games-frame-pacing REQUIRED CONFIG)
add_library(launcherfix SHARED launcher-fix.cpp patcher.cpp pacing.cpp)
target_compile_features(launcherfix PUBLIC cxx_std_20) target_compile_features(launcherfix PUBLIC cxx_std_20)
set_target_properties(launcherfix PROPERTIES CXX_EXTENSIONS OFF) set_target_properties(launcherfix PROPERTIES CXX_EXTENSIONS OFF)
@@ -14,4 +16,5 @@ set_target_properties(launcherfix PROPERTIES
CXX_VISIBILITY_PRESET hidden CXX_VISIBILITY_PRESET hidden
) )
target_link_libraries(launcherfix PRIVATE log) target_link_libraries(launcherfix PRIVATE log android)
target_link_libraries(launcherfix PRIVATE games-frame-pacing::swappy_static)
+65
View File
@@ -0,0 +1,65 @@
#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,106 +2,33 @@ package com.customRobTop
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.ActivityNotFoundException import android.content.*
import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.net.ConnectivityManager import android.net.ConnectivityManager
import android.net.NetworkCapabilities import android.net.NetworkCapabilities
import android.net.Uri
import android.os.Build import android.os.Build
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import android.provider.Settings import android.provider.Settings
import android.widget.Toast 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 androidx.core.net.toUri
import com.customRobTop.contracts.ImportFileActivityContract
import com.customRobTop.contracts.ShareFileActivityContract
import com.geode.launcher.R 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.closeIMEKeyboard
import org.cocos2dx.lib.Cocos2dxGLSurfaceView.Companion.openIMEKeyboard 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.lang.ref.WeakReference
import java.util.UUID import java.util.*
@Suppress("unused", "UNUSED_PARAMETER") @Suppress("unused", "UNUSED_PARAMETER")
object BaseRobTopActivity { 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 private var isLoaded = false
var blockBackButton = false var blockBackButton = false
private var keyboardActive = false private var keyboardActive = false
var curKeyboardInputFlags = 0
var isPaused = false var isPaused = false
lateinit var me: WeakReference<Activity> lateinit var me: WeakReference<Activity>
private var shouldResumeSound = true private var shouldResumeSound = true
private var pendingExportPaths = emptyList<String>() fun setCurrentActivity(currentActivity: Activity) {
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) 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") @SuppressLint("HardwareIds")
@@ -160,18 +87,6 @@ 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 @JvmStatic
fun sendMail(subject: String, body: String, to: String) { fun sendMail(subject: String, body: String, to: String) {
me.get()?.runOnUiThread { me.get()?.runOnUiThread {
@@ -349,164 +264,4 @@ object BaseRobTopActivity {
level = DeprecationLevel.HIDDEN level = DeprecationLevel.HIDDEN
) )
fun logEvent(event: String) {} 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,16 +55,4 @@ object JniToCpp {
@JvmStatic @JvmStatic
external fun videoAdShowed() external fun videoAdShowed()
@JvmStatic
external fun filesPicked(files: Array<String>)
@JvmStatic
external fun fileShareFinished(success: Boolean)
@JvmStatic
external fun textInputCaretToEnd()
@JvmStatic
external fun textInputDone()
} }
@@ -1,40 +0,0 @@
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()
}
}
}
@@ -1,40 +0,0 @@
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,7 +5,9 @@ import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.opengl.GLSurfaceView import android.opengl.EGL14
import android.opengl.EGLConfig
import android.opengl.EGLDisplay
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.os.Environment import android.os.Environment
@@ -43,9 +45,6 @@ import java.io.File
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.IOException 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 { enum class DisplayMode {
DEFAULT, LIMITED, FULLSCREEN; DEFAULT, LIMITED, FULLSCREEN;
@@ -78,6 +77,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
private var mAspectRatio = 0.0f private var mAspectRatio = 0.0f
private var mScreenZoom = 1.0f private var mScreenZoom = 1.0f
private var mScreenZoomFit = false private var mScreenZoomFit = false
private var mNativeFramePacing = false
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
setupUIState() setupUIState()
@@ -131,6 +131,10 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
} }
}) })
mGLSurfaceView?.manualBackEvents = true mGLSurfaceView?.manualBackEvents = true
if (mNativeFramePacing) {
LauncherFix.initFramePacing(this)
}
} }
private fun createVersionFile() { private fun createVersionFile() {
@@ -171,8 +175,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME) tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME)
tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME) tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME)
val gameVersionCode = GamePackageUtils.getGameVersionCode(packageManager) if (GamePackageUtils.getGameVersionCode(packageManager) >= 39L) {
if (gameVersionCode >= 39L) {
// this fix requires geode v3, which is 2.206+ // 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 // there is a short period in which 2.206 users will still have geode v2, but whatever. ig
LauncherFix.enableExceptionsRenaming() LauncherFix.enableExceptionsRenaming()
@@ -183,9 +186,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
loadInternalMods() loadInternalMods()
val baseView = createView() val baseView = createView()
if (gameVersionCode >= 42L) {
mGLSurfaceView?.sendNewCocosEvents = true
}
if (!GeodeUtils.handleSafeArea && Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) { if (!GeodeUtils.handleSafeArea && Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
val frameLayoutParams = ViewGroup.LayoutParams( val frameLayoutParams = ViewGroup.LayoutParams(
@@ -209,22 +209,12 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
left = 0, top = 0, right = 0, bottom = 0 left = 0, top = 0, right = 0, bottom = 0
) )
} }
Cocos2dxGLSurfaceView.onKeyboardInsets(insets)
WindowInsets.CONSUMED WindowInsets.CONSUMED
} }
frameLayout.addView(baseView) frameLayout.addView(baseView)
setContentView(frameLayout) setContentView(frameLayout)
} else { } else {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
baseView.setOnApplyWindowInsetsListener { view, insets ->
Cocos2dxGLSurfaceView.onKeyboardInsets(insets)
return@setOnApplyWindowInsetsListener view.onApplyWindowInsets(insets)
}
}
setContentView(baseView) setContentView(baseView)
} }
@@ -505,16 +495,14 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
val frameRate = mLimitedRefreshRate val frameRate = mLimitedRefreshRate
if (frameRate != null) { if (frameRate != null) {
renderer.limitFrameRate(frameRate) glSurfaceView.setMaxFrameRate(frameRate)
} }
glSurfaceView.nativeFramePacing = mNativeFramePacing
editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
glSurfaceView.cocos2dxEditText = editText glSurfaceView.cocos2dxEditText = editText
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
glSurfaceView.defaultFocusHighlightEnabled = false
}
return frameLayout return frameLayout
} }
@@ -547,6 +535,8 @@ 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) { if (displayMode == DisplayMode.FULLSCREEN && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES window.attributes.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
GeodeUtils.handleSafeArea = true GeodeUtils.handleSafeArea = true
@@ -580,6 +570,10 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
override fun onDestroy() { override fun onDestroy() {
super.onDestroy() super.onDestroy()
FMOD.close() FMOD.close()
if (mNativeFramePacing) {
LauncherFix.destroyFramePacing()
}
} }
private fun resumeGame() { private fun resumeGame() {
@@ -751,11 +745,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
} }
} }
class EGLConfigChooser : GLSurfaceView.EGLConfigChooser { class EGLConfigChooser : ExplicitGLSurfaceView.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( private data class ConfigValues(
val redSize: Int, val redSize: Int,
val greenSize: Int, val greenSize: Int,
@@ -765,29 +755,29 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
val stencilSize: Int, val stencilSize: Int,
) )
private fun getAttribValue(egl: EGL10, display: EGLDisplay, config: EGLConfig, attrib: Int): Int { private fun getAttribValue(display: EGLDisplay, config: EGLConfig, attrib: Int): Int {
val value = IntArray(1) val value = IntArray(1)
egl.eglGetConfigAttrib(display, config, attrib, value) EGL14.eglGetConfigAttrib(display, config, attrib, value, 0)
return value[0] return value[0]
} }
private fun testConfig(egl: EGL10, display: EGLDisplay, attrib: ConfigValues): EGLConfig? { private fun testConfig(display: EGLDisplay, attrib: ConfigValues): EGLConfig? {
val attribList = intArrayOf( val attribList = intArrayOf(
EGL10.EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
EGL10.EGL_RED_SIZE, attrib.redSize, EGL14.EGL_RED_SIZE, attrib.redSize,
EGL10.EGL_GREEN_SIZE, attrib.greenSize, EGL14.EGL_GREEN_SIZE, attrib.greenSize,
EGL10.EGL_BLUE_SIZE, attrib.blueSize, EGL14.EGL_BLUE_SIZE, attrib.blueSize,
EGL10.EGL_ALPHA_SIZE, attrib.alphaSize, EGL14.EGL_ALPHA_SIZE, attrib.alphaSize,
EGL10.EGL_DEPTH_SIZE, attrib.depthSize, EGL14.EGL_DEPTH_SIZE, attrib.depthSize,
EGL10.EGL_STENCIL_SIZE, attrib.stencilSize, EGL14.EGL_STENCIL_SIZE, attrib.stencilSize,
EGL10.EGL_NONE EGL14.EGL_NONE
) )
val configs = arrayOfNulls<EGLConfig>(1) val configs = arrayOfNulls<EGLConfig>(1)
val numConfigs = IntArray(1) val numConfigs = IntArray(1)
val res = egl.eglChooseConfig(display, attribList, configs, configs.size, numConfigs) val res = EGL14.eglChooseConfig(display, attribList, 0, configs, 0, configs.size, numConfigs, 0)
if (res && numConfigs[0] > 0 && configs[0] != null) { if (res && numConfigs[0] > 0 && configs[0] != null) {
return configs[0] return configs[0]
} }
@@ -795,18 +785,18 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
return null return null
} }
private fun printConfig(egl: EGL10, display: EGLDisplay, config: EGLConfig) { private fun printConfig(display: EGLDisplay, config: EGLConfig) {
val id = getAttribValue(egl, display, config, EGL10.EGL_CONFIG_ID) val id = getAttribValue(display, config, EGL14.EGL_CONFIG_ID)
val red = getAttribValue(egl, display, config, EGL10.EGL_RED_SIZE) val red = getAttribValue(display, config, EGL14.EGL_RED_SIZE)
val green = getAttribValue(egl, display, config, EGL10.EGL_GREEN_SIZE) val green = getAttribValue(display, config, EGL14.EGL_GREEN_SIZE)
val blue = getAttribValue(egl, display, config, EGL10.EGL_BLUE_SIZE) val blue = getAttribValue(display, config, EGL14.EGL_BLUE_SIZE)
val alpha = getAttribValue(egl, display, config, EGL10.EGL_ALPHA_SIZE) val alpha = getAttribValue(display, config, EGL14.EGL_ALPHA_SIZE)
val depth = getAttribValue(egl, display, config, EGL10.EGL_DEPTH_SIZE) val depth = getAttribValue(display, config, EGL14.EGL_DEPTH_SIZE)
val stencil = getAttribValue(egl, display, config, EGL10.EGL_STENCIL_SIZE) val stencil = getAttribValue(display, config, EGL14.EGL_STENCIL_SIZE)
println("EGLConfig $id: (red = $red, green = $green, blue = $blue, alpha = $alpha, depth = $depth, stencil = $stencil)") println("EGLConfig $id: (red = $red, green = $green, blue = $blue, alpha = $alpha, depth = $depth, stencil = $stencil)")
} }
override fun chooseConfig(egl: EGL10, display: EGLDisplay): EGLConfig { override fun chooseConfig(display: EGLDisplay): EGLConfig {
val configs = listOf( val configs = listOf(
ConfigValues(8, 8, 8, 8, 16, 8), ConfigValues(8, 8, 8, 8, 16, 8),
ConfigValues(8, 8, 8, 0, 16, 8), ConfigValues(8, 8, 8, 0, 16, 8),
@@ -815,9 +805,9 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
) )
return configs.firstNotNullOf { return configs.firstNotNullOf {
val config = testConfig(egl, display, it) val config = testConfig(display, it)
if (config != null) { if (config != null) {
printConfig(egl, display, config) printConfig(display, config)
} }
config config
@@ -1,5 +1,7 @@
package com.geode.launcher package com.geode.launcher
import android.app.Activity
import android.view.Surface
import androidx.annotation.Keep import androidx.annotation.Keep
import com.geode.launcher.utils.Constants import com.geode.launcher.utils.Constants
@@ -16,4 +18,10 @@ object LauncherFix {
external fun enableExceptionsRenaming() external fun enableExceptionsRenaming()
external fun performPatches() 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,10 +104,8 @@ class MainActivity : ComponentActivity() {
val gdInstalled = GamePackageUtils.isGameInstalled(packageManager) val gdInstalled = GamePackageUtils.isGameInstalled(packageManager)
val geodeInstalled = LaunchUtils.isGeodeInstalled(this) val geodeInstalled = LaunchUtils.isGeodeInstalled(this)
val restartImmediately = intent.getBooleanExtra("restarted", false) if (gdInstalled && geodeInstalled && intent.getBooleanExtra("restarted", false)) {
if (gdInstalled && geodeInstalled && restartImmediately) { onLaunch(this)
val withSafeMode = intent.getBooleanExtra("safe-mode", false)
onLaunch(this, withSafeMode)
} }
enableEdgeToEdge() enableEdgeToEdge()
@@ -10,10 +10,7 @@ import android.provider.DocumentsContract
import android.provider.DocumentsProvider import android.provider.DocumentsProvider
import android.webkit.MimeTypeMap import android.webkit.MimeTypeMap
import androidx.annotation.RequiresApi 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.LaunchUtils
import com.geode.launcher.utils.PreferenceUtils
import java.io.File import java.io.File
private val DEFAULT_ROOT_PROJECTION: Array<String> = arrayOf( private val DEFAULT_ROOT_PROJECTION: Array<String> = arrayOf(
@@ -63,17 +60,8 @@ class UserDirectoryProvider : DocumentsProvider() {
DocumentsContract.Root.FLAG_SUPPORTS_RECENTS or DocumentsContract.Root.FLAG_SUPPORTS_RECENTS or
DocumentsContract.Root.FLAG_SUPPORTS_SEARCH DocumentsContract.Root.FLAG_SUPPORTS_SEARCH
) )
add(DocumentsContract.Root.COLUMN_TITLE, context.getString(R.string.app_name))
val selectedIconKey = PreferenceUtils.get(context).getString(PreferenceUtils.Key.SELECTED_ICON) add(DocumentsContract.Root.COLUMN_ICON, R.drawable.geode_logo)
?: "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 return result
@@ -408,8 +408,7 @@ fun LauncherUpdateIndicator(modifier: Modifier = Modifier) {
} }
} }
// there's like a single pixel of the shadow that gets clipped without this... ElevatedCard(modifier) {
ElevatedCard(modifier = modifier.padding(bottom = 1.dp)) {
Column( Column(
// buttons add enough padding already, lower to compensate // buttons add enough padding already, lower to compensate
modifier = Modifier.padding( modifier = Modifier.padding(
@@ -169,6 +169,12 @@ 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) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
SettingsCard( SettingsCard(
@@ -53,7 +53,6 @@ object GamePackageUtils {
39L to "2.206", 39L to "2.206",
40L to "2.2074", // ok 40L to "2.2074", // ok
41L to "2.2081", 41L to "2.2081",
42L to "2.209",
) )
/** /**
@@ -191,16 +191,10 @@ object GeodeUtils {
@JvmStatic @JvmStatic
fun restartGame() { fun restartGame() {
restartGame(false)
}
@JvmStatic
fun restartGame(safeMode: Boolean) {
activity.get()?.run { activity.get()?.run {
packageManager.getLaunchIntentForPackage(packageName)?.also { packageManager.getLaunchIntentForPackage(packageName)?.also {
val mainIntent = Intent.makeRestartActivityTask(it.component) val mainIntent = Intent.makeRestartActivityTask(it.component)
mainIntent.putExtra("restarted", true) mainIntent.putExtra("restarted", true)
mainIntent.putExtra("safe-mode", safeMode)
startActivity(mainIntent) startActivity(mainIntent)
exitProcess(0) exitProcess(0)
} }
@@ -800,7 +794,7 @@ object GeodeUtils {
external fun internalKeyEvent(timestamp: Long, deviceId: Int, eventSource: Int, keyCode: Int, modifiers: Int, isDown: Boolean, repeatCount: Int) 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 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, mouseX: Float, mouseY: Float) external fun internalScrollEvent(timestamp: Long, deviceId: Int, eventSource: Int, scrollX: Float, scrollY: 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 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) external fun inputDeviceAdded(deviceId: Int)
@@ -44,7 +44,6 @@ enum class ApplicationIcon {
GEODE, GEODE,
PRIDE, PRIDE,
TRANS, TRANS,
GREEN,
SAPPHIRE; SAPPHIRE;
companion object { companion object {
@@ -54,7 +53,6 @@ enum class ApplicationIcon {
"pride" -> PRIDE "pride" -> PRIDE
"trans" -> TRANS "trans" -> TRANS
"sapphire" -> SAPPHIRE "sapphire" -> SAPPHIRE
"green" -> GREEN
else -> DEFAULT else -> DEFAULT
} }
} }
@@ -65,7 +63,6 @@ enum class ApplicationIcon {
DEFAULT -> "default" DEFAULT -> "default"
TRANS -> "trans" TRANS -> "trans"
SAPPHIRE -> "sapphire" SAPPHIRE -> "sapphire"
GREEN -> "green"
} }
} }
@@ -108,11 +105,5 @@ object IconUtils {
iconId = R.mipmap.ic_launcher_sapphire, iconId = R.mipmap.ic_launcher_sapphire,
nameId = R.string.application_icon_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,6 +150,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
LAST_LAUNCHER_UPDATE, LAST_LAUNCHER_UPDATE,
DISABLE_UPDATE_CACHE, DISABLE_UPDATE_CACHE,
PREBUNDLED_GEODE_MIGRATION_PERFORMED, PREBUNDLED_GEODE_MIGRATION_PERFORMED,
NATIVE_FRAME_PACING,
} }
private fun defaultValueForBooleanKey(key: Key): Boolean { private fun defaultValueForBooleanKey(key: Key): Boolean {
@@ -201,6 +202,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
Key.LAST_LAUNCHER_UPDATE -> "PreferenceLastLauncherUpdate" Key.LAST_LAUNCHER_UPDATE -> "PreferenceLastLauncherUpdate"
Key.DISABLE_UPDATE_CACHE -> "PreferenceDisableUpdateCache" Key.DISABLE_UPDATE_CACHE -> "PreferenceDisableUpdateCache"
Key.PREBUNDLED_GEODE_MIGRATION_PERFORMED -> "PreferencePrebundledGeodeMigrationPerformed" Key.PREBUNDLED_GEODE_MIGRATION_PERFORMED -> "PreferencePrebundledGeodeMigrationPerformed"
Key.NATIVE_FRAME_PACING -> "PreferenceNativeFramePacing"
// Key.RELEASE_CHANNEL -> "PreferenceReleaseChannel" // Key.RELEASE_CHANNEL -> "PreferenceReleaseChannel"
// Key.LAST_DISMISSED_UPDATE -> "PreferenceLastDismissedUpdate" // Key.LAST_DISMISSED_UPDATE -> "PreferenceLastDismissedUpdate"
// Key.FORCE_HRR -> "PreferenceForceHighRefreshRate" // Key.FORCE_HRR -> "PreferenceForceHighRefreshRate"
@@ -8,73 +8,32 @@ import android.os.Build
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.os.Message import android.os.Message
import android.text.InputType import android.util.Log
import android.view.InputDevice import android.view.InputDevice
import android.view.KeyEvent import android.view.KeyEvent
import android.view.MotionEvent import android.view.MotionEvent
import android.view.Surface import android.view.Surface
import android.view.WindowInsets
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import androidx.annotation.RequiresApi import androidx.annotation.RequiresApi
import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatEditText
import com.customRobTop.BaseRobTopActivity import com.customRobTop.BaseRobTopActivity
import com.geode.launcher.ExplicitGLSurfaceView
import com.geode.launcher.LauncherFix
import com.geode.launcher.utils.GeodeUtils import com.geode.launcher.utils.GeodeUtils
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.roundToLong
private const val HANDLER_OPEN_IME_KEYBOARD = 2 private const val HANDLER_OPEN_IME_KEYBOARD = 2
private const val HANDLER_CLOSE_IME_KEYBOARD = 3 private const val HANDLER_CLOSE_IME_KEYBOARD = 3
private const val MS_TO_NS = 1_000_000 private const val MS_TO_NS = 1_000_000
private const val SEC_TO_NS = 1_000_000_000.0 private const val SEC_TO_NS = 1_000_000_000.0
fun inputTypeForFlags(i: Int): Int { class Cocos2dxGLSurfaceView(context: Context) : ExplicitGLSurfaceView(context) {
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 { companion object {
lateinit var cocos2dxGLSurfaceView: Cocos2dxGLSurfaceView lateinit var cocos2dxGLSurfaceView: Cocos2dxGLSurfaceView
private lateinit var handler: Handler private lateinit var handler: Handler
private lateinit var cocos2dxTextInputWrapper: Cocos2dxTextInputWrapper 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 @JvmStatic
fun openIMEKeyboard() { fun openIMEKeyboard() {
val msg = Message() val msg = Message()
@@ -94,30 +53,6 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
cocos2dxGLSurfaceView.queueEvent { cocos2dxGLSurfaceView.queueEvent {
Cocos2dxAccelerometer.onSensorChanged(x, y, z, timestamp) 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 private lateinit var cocos2dxRenderer: Cocos2dxRenderer
@@ -132,9 +67,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
var controllerEventsEnabled = false var controllerEventsEnabled = false
private set private set
var supportsResizeEvents = false var supportsResizeEvents = false
var saveNativeFramePacing = false
// 2.209+ events for keyboard and such
var sendNewCocosEvents = false
var cocos2dxEditText: AppCompatEditText? = null var cocos2dxEditText: AppCompatEditText? = null
set(value) { set(value) {
@@ -156,13 +89,9 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
HANDLER_OPEN_IME_KEYBOARD -> { HANDLER_OPEN_IME_KEYBOARD -> {
if (cocos2dxEditText?.requestFocus() == true) { if (cocos2dxEditText?.requestFocus() == true) {
cocos2dxEditText?.apply { cocos2dxEditText?.apply {
removeTextChangedListener(cocos2dxTextInputWrapper) removeTextChangedListener(
cocos2dxTextInputWrapper
if (sendNewCocosEvents) { )
inputType = inputTypeForFlags(BaseRobTopActivity.curKeyboardInputFlags)
imeOptions = imeOptionsForFlags(BaseRobTopActivity.curKeyboardInputFlags)
}
setText("") setText("")
val text = msg.obj as String val text = msg.obj as String
append(text) append(text)
@@ -175,13 +104,16 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
cocos2dxEditText, cocos2dxEditText,
0 0
) )
Log.d("GLSurfaceView", "showSoftInput")
return return
} }
return return
} }
HANDLER_CLOSE_IME_KEYBOARD -> { HANDLER_CLOSE_IME_KEYBOARD -> {
cocos2dxEditText?.apply { cocos2dxEditText?.apply {
removeTextChangedListener(cocos2dxTextInputWrapper) removeTextChangedListener(
cocos2dxTextInputWrapper
)
(context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow( (context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager).hideSoftInputFromWindow(
windowToken, windowToken,
0 0
@@ -189,10 +121,8 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
} }
requestFocus() requestFocus()
Log.d("GLSurfaceView", "HideSoftInput")
queueEvent {
cocos2dxRenderer.handleTextClosed() cocos2dxRenderer.handleTextClosed()
}
return return
} }
else -> return else -> return
@@ -213,6 +143,10 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
println("updateRefreshRate: selecting refresh rate of ${chosenDisplay.refreshRate} (display ${chosenDisplay.modeId})") 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) holder.surface.setFrameRate(chosenRefreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
if (isAttachedToWindow) { if (isAttachedToWindow) {
(context as Activity).window.attributes.preferredRefreshRate = chosenRefreshRate (context as Activity).window.attributes.preferredRefreshRate = chosenRefreshRate
@@ -227,6 +161,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
renderMode = RENDERMODE_CONTINUOUSLY renderMode = RENDERMODE_CONTINUOUSLY
queueEvent { cocos2dxRenderer.handleOnResume() } queueEvent { cocos2dxRenderer.handleOnResume() }
} }
@@ -371,97 +306,6 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(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 { private fun legacyKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean {
return when (keyCode) { return when (keyCode) {
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_MENU -> { KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_MENU -> {
@@ -519,21 +363,67 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
} }
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
if (!queueKeyboardEvent(keyCode, event, true)) { if (!useKeyboardEvents && !controllerEventsEnabled) {
return legacyKeyDown(keyCode, event) 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 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
}
}
}
override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean { override fun onKeyUp(keyCode: Int, event: KeyEvent): Boolean {
if (!queueKeyboardEvent(keyCode, event, false)) { if (!useKeyboardEvents && !controllerEventsEnabled) {
return super.onKeyUp(keyCode, event) 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 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
}
}
}
// i'm lazy. // i'm lazy.
// https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input // https://developer.android.com/develop/ui/views/touch-and-input/game-controllers/controller-input
private fun getCenteredAxis( private fun getCenteredAxis(
@@ -594,15 +484,11 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
} }
override fun onGenericMotionEvent(event: MotionEvent?): Boolean { override fun onGenericMotionEvent(event: MotionEvent?): Boolean {
val hasGeodeEvents = useKeyboardEvents || controllerEventsEnabled if (!useKeyboardEvents && !controllerEventsEnabled) {
if (!hasGeodeEvents && !sendNewCocosEvents) {
return super.onGenericMotionEvent(event) return super.onGenericMotionEvent(event)
} }
if (event?.action == MotionEvent.ACTION_SCROLL) { if (event?.action == MotionEvent.ACTION_SCROLL) {
val mouseX = event.x
val mouseY = event.y
val scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL) val scrollX = event.getAxisValue(MotionEvent.AXIS_HSCROLL)
val scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL) val scrollY = event.getAxisValue(MotionEvent.AXIS_VSCROLL)
val timestamp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) val timestamp = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
@@ -616,11 +502,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
} }
if (controllerEventsEnabled) { if (controllerEventsEnabled) {
GeodeUtils.internalScrollEvent(timestamp, event.deviceId, event.source, scrollX, scrollY, mouseX, mouseY) GeodeUtils.internalScrollEvent(timestamp, event.deviceId, event.source, scrollX, scrollY)
}
if (!hasGeodeEvents && sendNewCocosEvents) {
cocos2dxRenderer.handleScroll(mouseX, mouseY, scrollY, scrollX)
} }
} }
@@ -660,6 +542,11 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
setRenderer(this.cocos2dxRenderer) setRenderer(this.cocos2dxRenderer)
} }
override fun setNativeFramePacing(nativeFramePacing: Boolean) {
super.setNativeFramePacing(nativeFramePacing)
saveNativeFramePacing = nativeFramePacing
}
private fun getContentText(): String { private fun getContentText(): String {
return cocos2dxRenderer.getContentText() return cocos2dxRenderer.getContentText()
} }
@@ -694,6 +581,10 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
inputManager.unregisterInputDeviceListener(InputListener) inputManager.unregisterInputDeviceListener(InputListener)
} }
fun setMaxFrameRate(rate: Int) {
cocos2dxRenderer.limitFrameRate(rate)
}
object InputListener : InputManager.InputDeviceListener { object InputListener : InputManager.InputDeviceListener {
override fun onInputDeviceAdded(deviceId: Int) { override fun onInputDeviceAdded(deviceId: Int) {
GeodeUtils.inputDeviceAdded(deviceId) GeodeUtils.inputDeviceAdded(deviceId)
@@ -1,15 +1,14 @@
package org.cocos2dx.lib package org.cocos2dx.lib
import android.opengl.GLSurfaceView import android.opengl.EGLConfig
import android.os.Build import android.os.Build
import javax.microedition.khronos.egl.EGLConfig import com.geode.launcher.ExplicitGLSurfaceView
import javax.microedition.khronos.opengles.GL10
private const val NANOSECONDS_PER_SECOND = 1000000000L private const val NANOSECONDS_PER_SECOND = 1000000000L
private const val NANOSECONDS_PER_MICROSECOND = 1000000L private const val NANOSECONDS_PER_MICROSECOND = 1000000L
@Suppress("unused", "KotlinJniMissingFunction") @Suppress("unused", "KotlinJniMissingFunction")
class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceView.Renderer { class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : ExplicitGLSurfaceView.Renderer {
companion object { companion object {
@JvmStatic @JvmStatic
fun setAnimationInterval(@Suppress("UNUSED_PARAMETER") animationInterval: Double) { fun setAnimationInterval(@Suppress("UNUSED_PARAMETER") animationInterval: Double) {
@@ -28,29 +27,12 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceVi
@JvmStatic @JvmStatic
private external fun nativeKeyDown(keyCode: Int): Boolean 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 @JvmStatic
private external fun nativeOnPause() private external fun nativeOnPause()
@JvmStatic @JvmStatic
private external fun nativeOnResume() private external fun nativeOnResume()
@JvmStatic
private external fun nativeScroll(x: Float, y: Float, scrollY: Float, scrollX: Float)
@JvmStatic @JvmStatic
private external fun nativeRender() private external fun nativeRender()
@@ -97,12 +79,12 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceVi
screenHeight = surfaceHeight screenHeight = surfaceHeight
} }
override fun onSurfaceCreated(gl10: GL10?, eglConfig: EGLConfig?) { override fun onSurfaceCreated(eglConfig: EGLConfig?) {
nativeInit(screenWidth, screenHeight) nativeInit(screenWidth, screenHeight)
lastTickInNanoSeconds = System.nanoTime() lastTickInNanoSeconds = System.nanoTime()
} }
override fun onSurfaceChanged(gl10: GL10?, width: Int, height: Int) { override fun onSurfaceChanged(width: Int, height: Int) {
if (setFrameRate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { if (setFrameRate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
handler.updateRefreshRate() handler.updateRefreshRate()
} }
@@ -110,7 +92,7 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceVi
println("renderer surfaceChanged: ${width}x${height}@${handler.display.refreshRate}fps") println("renderer surfaceChanged: ${width}x${height}@${handler.display.refreshRate}fps")
} }
override fun onDrawFrame(gl: GL10?) { override fun onDrawFrame() {
val animationInterval = mAnimationInterval val animationInterval = mAnimationInterval
if (animationInterval == null) { if (animationInterval == null) {
nativeRender() nativeRender()
@@ -150,18 +132,6 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceVi
nativeKeyDown(keyCode) 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() { fun handleOnPause() {
nativeOnPause() nativeOnPause()
} }
@@ -5,10 +5,8 @@ import android.text.TextWatcher
import android.widget.TextView.OnEditorActionListener import android.widget.TextView.OnEditorActionListener
import android.text.Editable import android.text.Editable
import android.view.KeyEvent import android.view.KeyEvent
import android.view.inputmethod.EditorInfo
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.TextView import android.widget.TextView
import com.customRobTop.JniToCpp
class Cocos2dxTextInputWrapper(private val cocos2dxGLSurfaceView: Cocos2dxGLSurfaceView) : TextWatcher, OnEditorActionListener { class Cocos2dxTextInputWrapper(private val cocos2dxGLSurfaceView: Cocos2dxGLSurfaceView) : TextWatcher, OnEditorActionListener {
private var originText: String? = null private var originText: String? = null
@@ -21,33 +19,21 @@ class Cocos2dxTextInputWrapper(private val cocos2dxGLSurfaceView: Cocos2dxGLSurf
this.originText = originText this.originText = originText
} }
override fun afterTextChanged(editable: Editable) { override fun afterTextChanged(s: Editable) {
if (isFullScreenEdit) { if (!isFullScreenEdit) {
return var nModified = s.length - text!!.length
} if (nModified > 0) {
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( cocos2dxGLSurfaceView.insertText(
str.substring(unchangedUntil) s.subSequence(text!!.length, s.length).toString()
) )
} else {
while (nModified < 0) {
cocos2dxGLSurfaceView.deleteBackward()
nModified++
}
}
text = s.toString()
} }
text = str
} }
override fun beforeTextChanged( override fun beforeTextChanged(
@@ -61,45 +47,22 @@ class Cocos2dxTextInputWrapper(private val cocos2dxGLSurfaceView: Cocos2dxGLSurf
override fun onTextChanged(pCharSequence: CharSequence, start: Int, before: Int, count: Int) {} override fun onTextChanged(pCharSequence: CharSequence, start: Int, before: Int, count: Int) {}
override fun onEditorAction(textView: TextView, actionID: Int, keyEvent: KeyEvent?): Boolean { override fun onEditorAction(textView: TextView, actionID: Int, keyEvent: KeyEvent?): Boolean {
val fullscreenIme = cocos2dxGLSurfaceView.cocos2dxEditText == textView && isFullScreenEdit if (cocos2dxGLSurfaceView.cocos2dxEditText == textView && isFullScreenEdit) {
if (fullscreenIme) { for (i in originText!!.length downTo 1) {
var text = textView.text.toString()
if (originText != text || !cocos2dxGLSurfaceView.sendNewCocosEvents) {
if (cocos2dxGLSurfaceView.sendNewCocosEvents) {
cocos2dxGLSurfaceView.queueEvent {
JniToCpp.textInputCaretToEnd()
}
}
repeat(originText!!.length) {
cocos2dxGLSurfaceView.deleteBackward() cocos2dxGLSurfaceView.deleteBackward()
} }
var text = textView.text.toString()
if (!cocos2dxGLSurfaceView.sendNewCocosEvents) {
if (!text.endsWith('\n')) { if (!text.endsWith('\n')) {
text += '\n' text += '\n'
} }
}
if (text.isNotEmpty()) {
cocos2dxGLSurfaceView.insertText(text) cocos2dxGLSurfaceView.insertText(text)
} }
if (actionID != 6) {
return false
} }
}
if (fullscreenIme || actionID == EditorInfo.IME_ACTION_DONE) {
if (cocos2dxGLSurfaceView.sendNewCocosEvents) {
cocos2dxGLSurfaceView.queueEvent {
JniToCpp.textInputDone()
}
}
}
if (actionID == EditorInfo.IME_ACTION_DONE) {
cocos2dxGLSurfaceView.requestFocus() cocos2dxGLSurfaceView.requestFocus()
Cocos2dxGLSurfaceView.closeIMEKeyboard() Cocos2dxGLSurfaceView.closeIMEKeyboard()
}
return false return false
} }
} }
@@ -1,28 +1,28 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="1366" android:viewportWidth="1024"
android:viewportHeight="1366"> android:viewportHeight="1024">
<group> <group android:scaleX="0.75" android:scaleY="0.75" android:translateX="128" android:translateY="128">
<clip-path <clip-path
android:pathData="M0,0h1366v1366h-1366z"/> android:pathData="M0,0h1024v1024h-1024z"/>
<path <path
android:pathData="M0,0h1366v1366h-1366z" android:pathData="M0,0h1024v1024h-1024z"
android:fillColor="#F4D48E"/> android:fillColor="#F4D48E"/>
<path <path
android:pathData="M598,683m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0" android:pathData="M512,512m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
android:fillColor="#F4D48E"/> android:fillColor="#F4D48E"/>
<path <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: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:fillColor="#F4A47B"/> android:fillColor="#F4A47B"/>
<path <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: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:fillColor="#E27880"/> android:fillColor="#E27880"/>
<path <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: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:fillColor="#B9588F"/> android:fillColor="#B9588F"/>
<path <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: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:fillColor="#714A9A"/> android:fillColor="#714A9A"/>
</group> </group>
</vector> </vector>
@@ -1,29 +0,0 @@
<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,29 +1,28 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="1366" android:viewportWidth="1024"
android:viewportHeight="1366"> android:viewportHeight="1024">
<group> <group android:scaleX="0.75" android:scaleY="0.75" android:translateX="128" android:translateY="128">
<clip-path <clip-path
android:pathData="M0,0h1366v1366h-1366z"/> android:pathData="M0,0h1024v1024h-1024z"/>
<path <path
android:pathData="M0,0h1366v1366h-1366z" android:pathData="M0,0h1024v1024h-1024z"
android:fillColor="#CC7C7C"/> android:fillColor="#CC7C7C"/>
<path <path
android:pathData="M598,683m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0" android:pathData="M512,512m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
android:fillColor="#CC7C7C"/> android:fillColor="#CC7C7C"/>
<path <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: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:fillColor="#C6B471"/> android:fillColor="#C6B471"/>
<path <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: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:fillColor="#59AF7F"/> android:fillColor="#59AF7F"/>
<path <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: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:fillColor="#5F8CAB"/> android:fillColor="#5F8CAB"/>
<path <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: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:fillColor="#70558D"/> android:fillColor="#70558D"/>
</group> </group>
</vector> </vector>
@@ -1,29 +1,28 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp" android:width="108dp"
android:height="108dp" android:height="108dp"
android:viewportWidth="1366" android:viewportWidth="1024"
android:viewportHeight="1366"> android:viewportHeight="1024">
<group> <group android:scaleX="0.75" android:scaleY="0.75" android:translateX="128" android:translateY="128">
<clip-path <clip-path
android:pathData="M0,0h1366v1366h-1366z"/> android:pathData="M0,0h1024v1024h-1024z"/>
<path <path
android:pathData="M0,0h1366v1366h-1366z" android:pathData="M0,0h1024v1024h-1024z"
android:fillColor="#65B8C2"/> android:fillColor="#65B8C2"/>
<path <path
android:pathData="M598,683m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0" android:pathData="M512,512m-512,0a512,512 0,1 1,1024 0a512,512 0,1 1,-1024 0"
android:fillColor="#65B8C2"/> android:fillColor="#65B8C2"/>
<path <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: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:fillColor="#DEA5CD"/> android:fillColor="#DEA5CD"/>
<path <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: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:fillColor="#DEE8EA"/> android:fillColor="#DEE8EA"/>
<path <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: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:fillColor="#C485BD"/> android:fillColor="#C485BD"/>
<path <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: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:fillColor="#259CB4"/> android:fillColor="#259CB4"/>
</group> </group>
</vector> </vector>
@@ -1,5 +0,0 @@
<?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>
+3 -1
View File
@@ -298,6 +298,9 @@
<string name="preference_profiles_delete_alt">Delete</string> <string name="preference_profiles_delete_alt">Delete</string>
<string name="preference_profiles_delete_undo_alt">Undo</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 --> <!-- ApplicationLogsActivity strings -->
<string name="title_activity_application_logs">Application logs</string> <string name="title_activity_application_logs">Application logs</string>
<string name="application_logs_no_logs">No logs available.</string> <string name="application_logs_no_logs">No logs available.</string>
@@ -338,7 +341,6 @@
<string name="application_icon_pride">Pride</string> <string name="application_icon_pride">Pride</string>
<string name="application_icon_trans">Transgender</string> <string name="application_icon_trans">Transgender</string>
<string name="application_icon_sapphire">Sapphire</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">Change icon to %1$s?</string>
<string name="application_icon_confirm_description">Geode Launcher will close itself to change the application icon.</string> <string name="application_icon_confirm_description">Geode Launcher will close itself to change the application icon.</string>
+4 -4
View File
@@ -1,9 +1,9 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules. // Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins { plugins {
id("com.android.application") version "9.4.1" apply false id("com.android.application") version "9.4.0" apply false
id("com.android.library") version "9.4.1" apply false id("com.android.library") version "9.4.0" apply false
id("org.jetbrains.kotlin.plugin.serialization") version "2.4.21" apply false id("org.jetbrains.kotlin.plugin.serialization") version "2.4.10" apply false
id("org.jetbrains.kotlin.plugin.compose") version "2.4.21" apply false id("org.jetbrains.kotlin.plugin.compose") version "2.4.10" apply false
} }
tasks.register<Delete>("clean") { tasks.register<Delete>("clean") {