Compare commits

...
10 Commits
Author SHA1 Message Date
Chloe 7d2b5e0091 add multiline input util 2025-06-26 16:56:58 -07:00
Chloe 0b92f4a318 implement internal callbacks 2025-06-22 14:45:04 -07:00
Chloe 7d75825d01 this breaks things and might fix others 2025-06-07 19:38:54 -07:00
Chloe c77271f45c some text input changes 2025-06-07 19:38:01 -07:00
Chloe 09bb28d66f remove unused import 2025-05-28 17:14:33 -07:00
Chloe 157952b6f7 download to temp file first 2025-05-28 17:11:22 -07:00
Chloe 352ff63ab0 also send timestamp for back 2025-05-03 16:33:19 -07:00
Chloe 91fc804dee bump version 2025-05-03 16:24:55 -07:00
Chloe 87808b56b3 remove premature bug fix 2025-05-03 16:22:21 -07:00
Chloe 2c2884c3b8 use indeterminate state instead of ellipses 2025-05-03 16:14:12 -07:00
9 changed files with 126 additions and 78 deletions

No files matched your search

+2 -2
View File
@@ -20,8 +20,8 @@ android {
applicationId = "com.geode.launcher"
minSdk = 23
targetSdk = 35
versionCode = 21
versionName = "1.6.0"
versionCode = 22
versionName = "1.6.1"
vectorDrawables {
useSupportLibrary = true
@@ -14,9 +14,10 @@ import android.util.Log
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.FrameLayout
import android.window.OnBackInvokedDispatcher
import androidx.activity.OnBackPressedCallback
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.AppCompatEditText
import androidx.compose.ui.platform.ComposeView
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
@@ -32,7 +33,6 @@ import com.geode.launcher.utils.GamePackageUtils
import com.geode.launcher.utils.GeodeUtils
import com.geode.launcher.utils.LaunchUtils
import com.geode.launcher.utils.PreferenceUtils
import org.cocos2dx.lib.Cocos2dxEditText
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
import org.cocos2dx.lib.Cocos2dxHelper
import org.cocos2dx.lib.Cocos2dxRenderer
@@ -123,13 +123,13 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
return
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
onBackInvokedDispatcher.registerOnBackInvokedCallback(OnBackInvokedDispatcher.PRIORITY_DEFAULT) {
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
mGLSurfaceView?.sendKeyBack()
}
})
mGLSurfaceView?.manualBackEvents = true
}
}
private fun createVersionFile() {
val versionPath = File(filesDir, "game_version.txt")
@@ -406,7 +406,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
val editText = Cocos2dxEditText(this)
val editText = AppCompatEditText(this)
editText.layoutParams = editTextLayoutParams
frameLayout.addView(editText)
@@ -443,7 +443,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
renderer.limitFrameRate(frameRate)
}
editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
editText.inputType = InputType.TYPE_CLASS_TEXT or InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS
glSurfaceView.cocos2dxEditText = editText
return frameLayout
@@ -588,6 +588,10 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
mGLSurfaceView?.sendTimestampEvents = true
true
}
GeodeUtils.CAPABILITY_INTERNAL_CALLBACKS -> {
mGLSurfaceView?.sendInternalTimestampEvents = true
true
}
else -> false
}
}
@@ -370,10 +370,13 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
}
UpdateProgressIndicator(
stringResource(
if (outOf != null) stringResource(
R.string.release_fetch_downloading,
downloaded,
outOf ?: "…"
outOf
) else stringResource(
R.string.release_fetch_downloading_indeterminate,
downloaded,
),
modifier = modifier,
releaseViewModel = releaseViewModel,
@@ -181,6 +181,10 @@ class ReleaseManager private constructor(
// set an initial download size
_uiState.value = ReleaseManagerState.InDownload(0, releaseAsset.size)
val outputFile = getTempFile()
// clone the file instance as renameTo may move the original file
val tempFilePath = outputFile.path
try {
val fileStream = DownloadUtils.downloadStream(
httpClient,
@@ -189,23 +193,30 @@ class ReleaseManager private constructor(
_uiState.value = ReleaseManagerState.InDownload(progress, outOf)
}
outputFile.parentFile?.mkdirs()
val geodeFile = getGeodeOutputPath()
DownloadUtils.extractFileFromZipStream(
fileStream,
outputFile.outputStream(),
geodeFile.name
)
// work around a permission issue from adb push
if (geodeFile.exists()) {
geodeFile.delete()
}
geodeFile.parentFile?.mkdirs()
DownloadUtils.extractFileFromZipStream(
fileStream,
geodeFile.outputStream(),
geodeFile.name
)
outputFile.renameTo(geodeFile)
} catch (e: Exception) {
sendError(e)
return
} finally {
val tempFileClone = File(tempFilePath)
try {
if (tempFileClone.exists()) tempFileClone.delete()
} catch (_: Exception) { }
}
downloadLauncherUpdateIfNecessary()
@@ -325,6 +336,15 @@ class ReleaseManager private constructor(
return File(geodeDirectory, geodeName)
}
private fun getTempFile(): File {
val geodeName = LaunchUtils.geodeFilename
val geodeDirectory = LaunchUtils.getBaseDirectory(applicationContext)
val tempFile = File.createTempFile(geodeName, null, geodeDirectory)
return tempFile
}
/**
* Cancels the current update job.
*/
@@ -31,6 +31,7 @@ import com.geode.launcher.UserDirectoryProvider
import com.geode.launcher.activityresult.GeodeOpenFileActivityResult
import com.geode.launcher.activityresult.GeodeOpenFilesActivityResult
import com.geode.launcher.activityresult.GeodeSaveFileActivityResult
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
import java.io.File
import java.lang.ref.WeakReference
import kotlin.system.exitProcess
@@ -542,6 +543,7 @@ object GeodeUtils {
const val CAPABILITY_EXTENDED_INPUT = "extended_input"
const val CAPABILITY_TIMESTAMP_INPUT = "timestamp_inputs"
const val CAPABILITY_INTERNAL_CALLBACKS = "internal_callbacks_v1"
private var capabilityListener: WeakReference<CapabilityListener?> = WeakReference(null)
@@ -571,6 +573,11 @@ object GeodeUtils {
.launchUrl(activity, url.toUri())
}
@JvmStatic
fun openMultilineIMEKeyboard() {
Cocos2dxGLSurfaceView.openMultilineIMEKeyboard()
}
external fun nativeKeyUp(keyCode: Int, modifiers: Int)
external fun nativeKeyDown(keyCode: Int, modifiers: Int, isRepeating: Boolean)
external fun nativeActionScroll(scrollX: Float, scrollY: Float)
@@ -582,4 +589,5 @@ object GeodeUtils {
* @see reportPlatformCapability
*/
external fun setNextInputTimestamp(timestamp: Long)
external fun setNextInputTimestampInternal(timestamp: Long)
}
@@ -1,18 +0,0 @@
package org.cocos2dx.lib
import android.content.Context
import android.view.KeyEvent
import androidx.appcompat.widget.AppCompatEditText
class Cocos2dxEditText(context: Context) : AppCompatEditText(context) {
var cocos2dxGLSurfaceView: Cocos2dxGLSurfaceView? = null
override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean {
super.onKeyDown(keyCode, keyEvent)
if (keyCode != KeyEvent.KEYCODE_BACK) {
return true
}
cocos2dxGLSurfaceView?.requestFocus()
return true
}
}
@@ -7,17 +7,21 @@ import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.text.InputType
import android.util.Log
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.Surface
import android.view.inputmethod.InputMethodManager
import androidx.annotation.RequiresApi
import androidx.appcompat.widget.AppCompatEditText
import com.customRobTop.BaseRobTopActivity
import com.geode.launcher.utils.GeodeUtils
private const val HANDLER_OPEN_IME_KEYBOARD = 2
private const val HANDLER_CLOSE_IME_KEYBOARD = 3
private const val HANDLER_ARG_MULTILINE = 1
private const val HANDLER_ARG_SINGLELINE = 0
private const val MS_TO_NS = 1_000_000
class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
@@ -26,11 +30,22 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
private lateinit var handler: Handler
private lateinit var cocos2dxTextInputWrapper: Cocos2dxTextInputWrapper
fun openMultilineIMEKeyboard() {
val msg = Message().apply {
what = HANDLER_OPEN_IME_KEYBOARD
obj = cocos2dxGLSurfaceView.getContentText()
arg1 = HANDLER_ARG_MULTILINE
}
handler.sendMessage(msg)
}
@JvmStatic
fun openIMEKeyboard() {
val msg = Message()
msg.what = HANDLER_OPEN_IME_KEYBOARD
msg.obj = cocos2dxGLSurfaceView.getContentText()
val msg = Message().apply {
what = HANDLER_OPEN_IME_KEYBOARD
obj = cocos2dxGLSurfaceView.getContentText()
arg1 = HANDLER_ARG_SINGLELINE
}
handler.sendMessage(msg)
}
@@ -55,17 +70,29 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
}
var sendTimestampEvents = false
var sendInternalTimestampEvents = false
var manualBackEvents = false
var cocos2dxEditText: Cocos2dxEditText? = null
var cocos2dxEditText: AppCompatEditText? = null
set(value) {
field = value
field?.setOnEditorActionListener(cocos2dxTextInputWrapper)
field?.cocos2dxGLSurfaceView = this
requestFocus()
}
fun setKeyboardMultiline() {
val editText = cocos2dxEditText ?: return
editText.inputType = editText.inputType or InputType.TYPE_TEXT_FLAG_MULTI_LINE
editText.maxLines = Int.MAX_VALUE
}
fun setKeyboardSingleLine() {
val editText = cocos2dxEditText ?: return
editText.inputType = editText.inputType and InputType.TYPE_TEXT_FLAG_MULTI_LINE.inv()
editText.maxLines = 1
}
fun initView() {
setEGLContextClientVersion(2)
isFocusableInTouchMode = true
@@ -77,6 +104,12 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
when (msg.what) {
HANDLER_OPEN_IME_KEYBOARD -> {
if (cocos2dxEditText?.requestFocus() == true) {
if (msg.arg1 == HANDLER_ARG_MULTILINE) {
setKeyboardMultiline()
} else {
setKeyboardSingleLine()
}
cocos2dxEditText?.apply {
removeTextChangedListener(
cocos2dxTextInputWrapper
@@ -150,6 +183,14 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
queueEvent { cocos2dxRenderer.handleOnResume() }
}
private fun sendNextEventTimestamp(timestamp: Long) {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
if (sendInternalTimestampEvents)
GeodeUtils.setNextInputTimestampInternal(timestamp)
}
override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
val pointerNumber = motionEvent.pointerCount
val ids = IntArray(pointerNumber)
@@ -169,9 +210,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
val xDown = xs[0]
val f = ys[0]
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
sendNextEventTimestamp(timestamp)
cocos2dxRenderer.handleActionDown(idDown, xDown, f)
}
true
@@ -181,27 +220,21 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
val f2 = xs[0]
val f3 = ys[0]
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
sendNextEventTimestamp(timestamp)
cocos2dxRenderer.handleActionUp(idUp, f2, f3)
}
true
}
MotionEvent.ACTION_MOVE -> {
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
sendNextEventTimestamp(timestamp)
cocos2dxRenderer.handleActionMove(ids, xs, ys)
}
true
}
MotionEvent.ACTION_CANCEL -> {
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
sendNextEventTimestamp(timestamp)
cocos2dxRenderer.handleActionCancel(ids, xs, ys)
}
true
@@ -212,9 +245,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
val xPointerDown = motionEvent.getX(indexPointerDown)
val y = motionEvent.getY(indexPointerDown)
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
sendNextEventTimestamp(timestamp)
cocos2dxRenderer.handleActionDown(idPointerDown, xPointerDown, y)
}
true
@@ -225,9 +256,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
val xPointerUp = motionEvent.getX(indexPointUp)
val y2 = motionEvent.getY(indexPointUp)
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
sendNextEventTimestamp(timestamp)
cocos2dxRenderer.handleActionUp(idPointerUp, xPointerUp, y2)
}
true
@@ -250,7 +279,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
private fun legacyKeyDown(keyCode: Int, keyEvent: KeyEvent): Boolean {
return when (keyCode) {
KeyEvent.KEYCODE_BACK, KeyEvent.KEYCODE_MENU -> {
if (keyEvent.repeatCount != 0 || BaseRobTopActivity.blockBackButton || manualBackEvents) {
if (keyEvent.repeatCount != 0 || BaseRobTopActivity.blockBackButton) {
return true
}
queueEvent { cocos2dxRenderer.handleKeyDown(keyCode) }
@@ -261,6 +290,10 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
}
fun sendKeyBack() {
if (cocos2dxEditText?.isFocused == true) {
requestFocus()
}
if (BaseRobTopActivity.blockBackButton) {
return
}
@@ -270,7 +303,9 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
cocos2dxRenderer.handleKeyDown(KeyEvent.KEYCODE_BACK)
}
} else {
val currentTime = System.nanoTime()
queueEvent {
sendNextEventTimestamp(currentTime)
GeodeUtils.nativeKeyDown(KeyEvent.KEYCODE_BACK, 0, false)
}
}
@@ -287,14 +322,12 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
super.onKeyDown(keyCode, event)
}
else -> {
if (BaseRobTopActivity.blockBackButton || manualBackEvents) {
if (BaseRobTopActivity.blockBackButton) {
return true
}
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(event.eventTime * MS_TO_NS)
sendNextEventTimestamp(event.eventTime * MS_TO_NS)
GeodeUtils.nativeKeyDown(keyCode, event.modifiers, event.repeatCount != 0)
}
true
@@ -317,9 +350,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
}
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(event.eventTime * MS_TO_NS)
sendNextEventTimestamp(event.eventTime * MS_TO_NS)
GeodeUtils.nativeKeyUp(keyCode, event.modifiers)
}
@@ -340,9 +371,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
event.eventTimeNanos else event.eventTime * MS_TO_NS
queueEvent {
if (sendTimestampEvents)
GeodeUtils.setNextInputTimestamp(timestamp)
sendNextEventTimestamp(timestamp)
GeodeUtils.nativeActionScroll(scrollX, scrollY)
}
@@ -5,6 +5,7 @@ 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
@@ -47,22 +48,22 @@ 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 (actionID != EditorInfo.IME_ACTION_DONE) {
return false
}
if (cocos2dxGLSurfaceView.cocos2dxEditText == textView && isFullScreenEdit) {
for (i in originText!!.length downTo 1) {
cocos2dxGLSurfaceView.deleteBackward()
}
var text = textView.text.toString()
if (text.compareTo("") == 0) {
text = "\n"
}
if (10 != text[text.length - 1].code) {
text += 10.toChar()
if (!text.endsWith('\n')) {
text += '\n'
}
cocos2dxGLSurfaceView.insertText(text)
}
if (actionID != 6) {
return false
}
cocos2dxGLSurfaceView.requestFocus()
Cocos2dxGLSurfaceView.closeIMEKeyboard()
return false
+1
View File
@@ -84,6 +84,7 @@
<string name="release_fetch_in_progress">Checking for new releases…</string>
<string name="release_fetch_success">Geode updated!</string>
<string name="release_fetch_downloading">Downloading update… (%1$s/%2$s)</string>
<string name="release_fetch_downloading_indeterminate">Downloading update… (%1$s)</string>
<string name="release_fetch_cancelling">Cancelling update…</string>
<string name="release_fetch_no_internet">No internet found.</string>
<string name="release_fetch_cancelled">Update cancelled.</string>