mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-23 00:42:38 +00:00
Compare commits
22
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d2b5e0091 | ||
|
|
0b92f4a318 | ||
|
|
7d75825d01 | ||
|
|
c77271f45c | ||
|
|
09bb28d66f | ||
|
|
157952b6f7 | ||
|
|
352ff63ab0 | ||
|
|
91fc804dee | ||
|
|
87808b56b3 | ||
|
|
2c2884c3b8 | ||
|
|
74e91e7dc1 | ||
|
|
e3ffbf311e | ||
|
|
5ad77bca57 | ||
|
|
3be0127e04 | ||
|
|
458c837aba | ||
|
|
67ca7707d8 | ||
|
|
776a4fa468 | ||
|
|
9528eef313 | ||
|
|
9827fe36c1 | ||
|
|
ce44270b7a | ||
|
|
34e324c54d | ||
|
|
986e33ce03 |
No files matched your search
+17
-15
@@ -14,14 +14,14 @@ plugins {
|
|||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdk = 35
|
compileSdk = 36
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId = "com.geode.launcher"
|
applicationId = "com.geode.launcher"
|
||||||
minSdk = 23
|
minSdk = 23
|
||||||
targetSdk = 35
|
targetSdk = 35
|
||||||
versionCode = 20
|
versionCode = 22
|
||||||
versionName = "1.5.0"
|
versionName = "1.6.1"
|
||||||
|
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
useSupportLibrary = true
|
useSupportLibrary = true
|
||||||
@@ -30,7 +30,7 @@ android {
|
|||||||
@Suppress("UnstableApiUsage")
|
@Suppress("UnstableApiUsage")
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
arguments("-DUSE_TULIPHOOK:BOOL=OFF", "-DANDROID_STL=c++_shared")
|
arguments("-DUSE_TULIPHOOK:BOOL=OFF", "-DANDROID_STL=c++_shared", "-DANDROID_SUPPORT_FLEXIBLE_PAGE_SIZES:BOOL=OFF")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,32 +82,34 @@ android {
|
|||||||
}
|
}
|
||||||
externalNativeBuild {
|
externalNativeBuild {
|
||||||
cmake {
|
cmake {
|
||||||
|
version = "3.25.0+"
|
||||||
path = file("src/main/cpp/CMakeLists.txt")
|
path = file("src/main/cpp/CMakeLists.txt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
namespace = "com.geode.launcher"
|
namespace = "com.geode.launcher"
|
||||||
ndkVersion = "27.2.12479018"
|
ndkVersion = "28.1.13356709"
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation (platform("androidx.compose:compose-bom:$composeBOM"))
|
implementation (platform("androidx.compose:compose-bom:$composeBOM"))
|
||||||
implementation ("androidx.core:core-ktx:1.15.0")
|
implementation ("androidx.core:core-ktx:1.16.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")
|
||||||
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
|
implementation ("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
|
||||||
implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
|
implementation ("androidx.lifecycle:lifecycle-viewmodel-compose:2.8.7")
|
||||||
implementation ("androidx.activity:activity-compose:1.10.0")
|
implementation ("androidx.activity:activity-compose:1.10.1")
|
||||||
implementation ("androidx.activity:activity-ktx:1.10.0")
|
implementation ("androidx.activity:activity-ktx:1.10.1")
|
||||||
implementation ("androidx.appcompat:appcompat:1.7.0")
|
implementation ("androidx.appcompat:appcompat:1.7.0")
|
||||||
implementation ("androidx.documentfile:documentfile:1.0.1")
|
implementation ("androidx.documentfile:documentfile:1.0.1")
|
||||||
implementation ("com.squareup.okio:okio:3.10.2")
|
implementation ("com.squareup.okio:okio:3.11.0")
|
||||||
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
|
implementation ("com.squareup.okhttp3:okhttp:4.12.0")
|
||||||
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.0")
|
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
|
||||||
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.8.0")
|
implementation ("org.jetbrains.kotlinx:kotlinx-serialization-json-okio:1.8.1")
|
||||||
implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.6.1")
|
implementation ("org.jetbrains.kotlinx:kotlinx-datetime:0.6.2")
|
||||||
implementation ("com.mikepenz:multiplatform-markdown-renderer-android:0.30.0")
|
implementation ("com.mikepenz:multiplatform-markdown-renderer-android:0.33.0")
|
||||||
implementation ("com.mikepenz:multiplatform-markdown-renderer-m3:0.30.0")
|
implementation ("com.mikepenz:multiplatform-markdown-renderer-m3:0.33.0")
|
||||||
|
implementation ("androidx.browser:browser:1.8.0")
|
||||||
debugImplementation ("androidx.compose.ui:ui-tooling")
|
debugImplementation ("androidx.compose.ui:ui-tooling")
|
||||||
coreLibraryDesugaring ("com.android.tools:desugar_jdk_libs:2.1.4")
|
coreLibraryDesugaring ("com.android.tools:desugar_jdk_libs:2.1.5")
|
||||||
}
|
}
|
||||||
@@ -39,8 +39,9 @@
|
|||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:hasFragileUserData="true"
|
android:hasFragileUserData="true"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:enableOnBackInvokedCallback="true"
|
||||||
android:theme="@style/Theme.GeodeLauncher"
|
android:theme="@style/Theme.GeodeLauncher"
|
||||||
tools:targetApi="31">
|
tools:targetApi="35">
|
||||||
<activity
|
<activity
|
||||||
android:name=".AltMainActivity"
|
android:name=".AltMainActivity"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ option(USE_TULIPHOOK "enables choice of hooking library" OFF)
|
|||||||
if (USE_TULIPHOOK)
|
if (USE_TULIPHOOK)
|
||||||
FetchContent_Declare(TulipHook
|
FetchContent_Declare(TulipHook
|
||||||
GIT_REPOSITORY https://github.com/geode-sdk/TulipHook.git
|
GIT_REPOSITORY https://github.com/geode-sdk/TulipHook.git
|
||||||
GIT_TAG 0021301fcea265416fa8eb5372694931c1d410cb
|
GIT_TAG 9aa1f95091ebac18c657d57b755f59d00fe6f1f8
|
||||||
)
|
)
|
||||||
|
|
||||||
set(TULIP_LINK_SOURCE ON CACHE INTERNAL "")
|
set(TULIP_LINK_SOURCE ON CACHE INTERNAL "")
|
||||||
@@ -24,8 +24,9 @@ if (USE_TULIPHOOK)
|
|||||||
target_link_libraries(launcherfix PRIVATE TulipHook TulipHookInclude)
|
target_link_libraries(launcherfix PRIVATE TulipHook TulipHookInclude)
|
||||||
else()
|
else()
|
||||||
FetchContent_Declare(dobby
|
FetchContent_Declare(dobby
|
||||||
|
# this is the last version that builds on android at all
|
||||||
GIT_REPOSITORY https://github.com/jmpews/Dobby
|
GIT_REPOSITORY https://github.com/jmpews/Dobby
|
||||||
GIT_TAG 6a80a61ea062424d1595613058a174c64454fa57
|
GIT_TAG 0932d69c320e786672361ab53825ba8f4245e9d3
|
||||||
)
|
)
|
||||||
|
|
||||||
FetchContent_GetProperties(dobby)
|
FetchContent_GetProperties(dobby)
|
||||||
|
|||||||
@@ -109,7 +109,7 @@ bool patch_symbol(std::uint32_t* hash_table, char* str_table, ElfW(Sym)* sym_tab
|
|||||||
// we found it! now go rename the symbol
|
// we found it! now go rename the symbol
|
||||||
|
|
||||||
#ifdef USE_TULIPHOOK
|
#ifdef USE_TULIPHOOK
|
||||||
auto res = tulip::hook::writeMemory(sym_name, new_symbol.data(), new_symbol.size());
|
auto res = tulip::hook::writeMemory(sym_name, SYMBOL_PATCH_VALUE.data(), SYMBOL_PATCH_VALUE.size());
|
||||||
if (!res) {
|
if (!res) {
|
||||||
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-fix", "failed to patch symbol: %s", res.unwrapErr().c_str());
|
__android_log_print(ANDROID_LOG_WARN, "GeodeLauncher-fix", "failed to patch symbol: %s", res.unwrapErr().c_str());
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import android.util.Log
|
|||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import android.widget.FrameLayout
|
import android.widget.FrameLayout
|
||||||
|
import androidx.activity.OnBackPressedCallback
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.appcompat.widget.AppCompatEditText
|
||||||
import androidx.compose.ui.platform.ComposeView
|
import androidx.compose.ui.platform.ComposeView
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.core.view.WindowInsetsCompat
|
import androidx.core.view.WindowInsetsCompat
|
||||||
@@ -31,7 +33,6 @@ import com.geode.launcher.utils.GamePackageUtils
|
|||||||
import com.geode.launcher.utils.GeodeUtils
|
import com.geode.launcher.utils.GeodeUtils
|
||||||
import com.geode.launcher.utils.LaunchUtils
|
import com.geode.launcher.utils.LaunchUtils
|
||||||
import com.geode.launcher.utils.PreferenceUtils
|
import com.geode.launcher.utils.PreferenceUtils
|
||||||
import org.cocos2dx.lib.Cocos2dxEditText
|
|
||||||
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
|
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
|
||||||
import org.cocos2dx.lib.Cocos2dxHelper
|
import org.cocos2dx.lib.Cocos2dxHelper
|
||||||
import org.cocos2dx.lib.Cocos2dxRenderer
|
import org.cocos2dx.lib.Cocos2dxRenderer
|
||||||
@@ -70,7 +71,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
|||||||
private var mHasWindowFocus = false
|
private var mHasWindowFocus = false
|
||||||
|
|
||||||
private var displayMode = DisplayMode.DEFAULT
|
private var displayMode = DisplayMode.DEFAULT
|
||||||
private var mChosenDisplayRate: Float? = null
|
private var mForceRefreshRate = false
|
||||||
private var mLimitedRefreshRate: Int? = null
|
private var mLimitedRefreshRate: Int? = null
|
||||||
private var mAspectRatio = 0.0f
|
private var mAspectRatio = 0.0f
|
||||||
private var mScreenZoom = 1.0f
|
private var mScreenZoom = 1.0f
|
||||||
@@ -121,6 +122,13 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
|||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
|
||||||
|
override fun handleOnBackPressed() {
|
||||||
|
mGLSurfaceView?.sendKeyBack()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
mGLSurfaceView?.manualBackEvents = true
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createVersionFile() {
|
private fun createVersionFile() {
|
||||||
@@ -398,7 +406,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
|||||||
ViewGroup.LayoutParams.MATCH_PARENT,
|
ViewGroup.LayoutParams.MATCH_PARENT,
|
||||||
ViewGroup.LayoutParams.WRAP_CONTENT
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
)
|
)
|
||||||
val editText = Cocos2dxEditText(this)
|
val editText = AppCompatEditText(this)
|
||||||
editText.layoutParams = editTextLayoutParams
|
editText.layoutParams = editTextLayoutParams
|
||||||
frameLayout.addView(editText)
|
frameLayout.addView(editText)
|
||||||
|
|
||||||
@@ -428,14 +436,14 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
|||||||
val renderer = Cocos2dxRenderer(glSurfaceView)
|
val renderer = Cocos2dxRenderer(glSurfaceView)
|
||||||
glSurfaceView.setCocos2dxRenderer(renderer)
|
glSurfaceView.setCocos2dxRenderer(renderer)
|
||||||
|
|
||||||
renderer.setFrameRate = mChosenDisplayRate
|
renderer.setFrameRate = mForceRefreshRate
|
||||||
|
|
||||||
val frameRate = mLimitedRefreshRate
|
val frameRate = mLimitedRefreshRate
|
||||||
if (frameRate != null) {
|
if (frameRate != null) {
|
||||||
renderer.limitFrameRate(frameRate)
|
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
|
glSurfaceView.cocos2dxEditText = editText
|
||||||
|
|
||||||
return frameLayout
|
return frameLayout
|
||||||
@@ -457,20 +465,24 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
|||||||
val limitedRefreshRate = preferenceUtils.getInt(PreferenceUtils.Key.LIMIT_FRAME_RATE).takeIf { it != 0 }
|
val limitedRefreshRate = preferenceUtils.getInt(PreferenceUtils.Key.LIMIT_FRAME_RATE).takeIf { it != 0 }
|
||||||
mLimitedRefreshRate = limitedRefreshRate
|
mLimitedRefreshRate = limitedRefreshRate
|
||||||
|
|
||||||
val forceRefreshRate = preferenceUtils.getBoolean(PreferenceUtils.Key.FORCE_HRR)
|
mForceRefreshRate = preferenceUtils.getBoolean(PreferenceUtils.Key.FORCE_HRR)
|
||||||
if (forceRefreshRate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
if (mForceRefreshRate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
var chosenDisplay = display.supportedModes?.maxByOrNull { it.refreshRate }
|
val chosenDisplay = display.supportedModes?.maxByOrNull { it.refreshRate }
|
||||||
if (chosenDisplay != null) {
|
if (chosenDisplay != null) {
|
||||||
var chosenRefreshRate = chosenDisplay.refreshRate
|
var chosenRefreshRate = chosenDisplay.refreshRate
|
||||||
println("Forcing a refresh rate of $chosenRefreshRate (display ${chosenDisplay.modeId})")
|
println("setupUIState: Forcing a refresh rate of $chosenRefreshRate (display ${chosenDisplay.modeId})")
|
||||||
|
|
||||||
window.attributes.preferredRefreshRate = chosenRefreshRate
|
window.attributes.preferredRefreshRate = chosenRefreshRate
|
||||||
mChosenDisplayRate = chosenRefreshRate
|
} else {
|
||||||
|
println("setupUIState could not find a display to select refresh rate of...")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
||||||
|
} else {
|
||||||
|
GeodeUtils.handleSafeArea = false
|
||||||
}
|
}
|
||||||
|
|
||||||
hideSystemUi()
|
hideSystemUi()
|
||||||
@@ -576,6 +588,10 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
|
|||||||
mGLSurfaceView?.sendTimestampEvents = true
|
mGLSurfaceView?.sendTimestampEvents = true
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
GeodeUtils.CAPABILITY_INTERNAL_CALLBACKS -> {
|
||||||
|
mGLSurfaceView?.sendInternalTimestampEvents = true
|
||||||
|
true
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -58,9 +58,9 @@ import com.geode.launcher.utils.LaunchUtils
|
|||||||
import com.geode.launcher.utils.PreferenceUtils
|
import com.geode.launcher.utils.PreferenceUtils
|
||||||
import com.mikepenz.markdown.compose.LocalBulletListHandler
|
import com.mikepenz.markdown.compose.LocalBulletListHandler
|
||||||
import com.mikepenz.markdown.m3.Markdown
|
import com.mikepenz.markdown.m3.Markdown
|
||||||
import com.mikepenz.markdown.m3.markdownColor
|
|
||||||
import com.mikepenz.markdown.m3.markdownTypography
|
import com.mikepenz.markdown.m3.markdownTypography
|
||||||
import kotlinx.datetime.toJavaInstant
|
import kotlinx.datetime.toJavaInstant
|
||||||
|
import org.intellij.markdown.flavours.gfm.GFMFlavourDescriptor
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.ConnectException
|
import java.net.ConnectException
|
||||||
import java.net.UnknownHostException
|
import java.net.UnknownHostException
|
||||||
@@ -191,17 +191,17 @@ fun LauncherUpdateInformation(onDismiss: () -> Unit) {
|
|||||||
style = Typography.titleLarge
|
style = Typography.titleLarge
|
||||||
)
|
)
|
||||||
|
|
||||||
CompositionLocalProvider(LocalBulletListHandler provides { _, _, _ -> "• " }) {
|
// every new release of the markdown library adds another arg to it
|
||||||
|
CompositionLocalProvider(LocalBulletListHandler provides { _, _, _, _, _ -> "• " }) {
|
||||||
Markdown(
|
Markdown(
|
||||||
content = nextRelease.release.body.replace("\r", ""),
|
content = nextRelease.release.body.replace("\r", ""),
|
||||||
colors = markdownColor(
|
|
||||||
linkText = MaterialTheme.colorScheme.primary
|
|
||||||
),
|
|
||||||
typography = markdownTypography(
|
typography = markdownTypography(
|
||||||
link = MaterialTheme.typography.bodyLarge.copy(
|
link = MaterialTheme.typography.bodyLarge.copy(
|
||||||
textDecoration = TextDecoration.Underline
|
textDecoration = TextDecoration.Underline,
|
||||||
)
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
),
|
||||||
),
|
),
|
||||||
|
flavour = GFMFlavourDescriptor(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -370,10 +370,13 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
|
|||||||
}
|
}
|
||||||
|
|
||||||
UpdateProgressIndicator(
|
UpdateProgressIndicator(
|
||||||
stringResource(
|
if (outOf != null) stringResource(
|
||||||
R.string.release_fetch_downloading,
|
R.string.release_fetch_downloading,
|
||||||
|
downloaded,
|
||||||
|
outOf
|
||||||
|
) else stringResource(
|
||||||
|
R.string.release_fetch_downloading_indeterminate,
|
||||||
downloaded,
|
downloaded,
|
||||||
outOf ?: "…"
|
|
||||||
),
|
),
|
||||||
modifier = modifier,
|
modifier = modifier,
|
||||||
releaseViewModel = releaseViewModel,
|
releaseViewModel = releaseViewModel,
|
||||||
|
|||||||
@@ -146,6 +146,11 @@ fun DeveloperSettingsScreen(onBackPressedDispatcher: OnBackPressedDispatcher?) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
OptionsGroup(title = stringResource(R.string.preference_category_updater)) {
|
OptionsGroup(title = stringResource(R.string.preference_category_updater)) {
|
||||||
|
SettingsCard(
|
||||||
|
title = stringResource(R.string.preference_use_index_update_api),
|
||||||
|
preferenceKey = PreferenceUtils.Key.USE_INDEX_API
|
||||||
|
)
|
||||||
|
|
||||||
SettingsSelectCard(
|
SettingsSelectCard(
|
||||||
title = stringResource(R.string.preference_release_channel_tag_name),
|
title = stringResource(R.string.preference_release_channel_tag_name),
|
||||||
dialogTitle = stringResource(R.string.preference_release_channel_select),
|
dialogTitle = stringResource(R.string.preference_release_channel_select),
|
||||||
|
|||||||
@@ -120,12 +120,24 @@ class ReleaseManager private constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val gameVersion = GamePackageUtils.getGameVersionCode(applicationContext.packageManager)
|
val gameVersion = GamePackageUtils.getGameVersionCode(applicationContext.packageManager)
|
||||||
val targetTag = getBestReleaseForGameVersion(gameVersion) ?: return null
|
val preferences = PreferenceUtils.get(applicationContext)
|
||||||
|
|
||||||
return when (targetTag) {
|
if (preferences.getBoolean(PreferenceUtils.Key.USE_INDEX_API)) {
|
||||||
TAG_LATEST -> releaseRepository.getLatestGeodeRelease()
|
val targetTag = preferences.getInt(PreferenceUtils.Key.RELEASE_CHANNEL_TAG)
|
||||||
TAG_BETA -> releaseRepository.getLatestGeodePreRelease()
|
|
||||||
else -> releaseRepository.getReleaseByTag(targetTag)
|
return when (targetTag) {
|
||||||
|
2 -> releaseRepository.getReleaseByTag("nightly")
|
||||||
|
1 -> releaseRepository.getLatestIndexRelease(gameVersion, true)
|
||||||
|
else -> releaseRepository.getLatestIndexRelease(gameVersion, false)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val targetTag = getBestReleaseForGameVersion(gameVersion) ?: return null
|
||||||
|
|
||||||
|
return when (targetTag) {
|
||||||
|
TAG_LATEST -> releaseRepository.getLatestGeodeRelease()
|
||||||
|
TAG_BETA -> releaseRepository.getLatestGeodePreRelease()
|
||||||
|
else -> releaseRepository.getReleaseByTag(targetTag)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -169,6 +181,10 @@ class ReleaseManager private constructor(
|
|||||||
// set an initial download size
|
// set an initial download size
|
||||||
_uiState.value = ReleaseManagerState.InDownload(0, releaseAsset.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 {
|
try {
|
||||||
val fileStream = DownloadUtils.downloadStream(
|
val fileStream = DownloadUtils.downloadStream(
|
||||||
httpClient,
|
httpClient,
|
||||||
@@ -177,23 +193,30 @@ class ReleaseManager private constructor(
|
|||||||
_uiState.value = ReleaseManagerState.InDownload(progress, outOf)
|
_uiState.value = ReleaseManagerState.InDownload(progress, outOf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outputFile.parentFile?.mkdirs()
|
||||||
|
|
||||||
val geodeFile = getGeodeOutputPath()
|
val geodeFile = getGeodeOutputPath()
|
||||||
|
|
||||||
|
DownloadUtils.extractFileFromZipStream(
|
||||||
|
fileStream,
|
||||||
|
outputFile.outputStream(),
|
||||||
|
geodeFile.name
|
||||||
|
)
|
||||||
|
|
||||||
// work around a permission issue from adb push
|
// work around a permission issue from adb push
|
||||||
if (geodeFile.exists()) {
|
if (geodeFile.exists()) {
|
||||||
geodeFile.delete()
|
geodeFile.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
geodeFile.parentFile?.mkdirs()
|
outputFile.renameTo(geodeFile)
|
||||||
|
|
||||||
DownloadUtils.extractFileFromZipStream(
|
|
||||||
fileStream,
|
|
||||||
geodeFile.outputStream(),
|
|
||||||
geodeFile.name
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
sendError(e)
|
sendError(e)
|
||||||
return
|
return
|
||||||
|
} finally {
|
||||||
|
val tempFileClone = File(tempFilePath)
|
||||||
|
try {
|
||||||
|
if (tempFileClone.exists()) tempFileClone.delete()
|
||||||
|
} catch (_: Exception) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
downloadLauncherUpdateIfNecessary()
|
downloadLauncherUpdateIfNecessary()
|
||||||
@@ -313,6 +336,15 @@ class ReleaseManager private constructor(
|
|||||||
return File(geodeDirectory, geodeName)
|
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.
|
* Cancels the current update job.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -165,10 +165,10 @@ class ReleaseRepository(private val httpClient: OkHttpClient) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
@OptIn(ExperimentalSerializationApi::class)
|
||||||
suspend fun getLatestIndexRelease(gd: String, prerelease: Boolean = false): DownloadableLoaderRelease? {
|
suspend fun getLatestIndexRelease(gd: Long, prerelease: Boolean = false): DownloadableLoaderRelease? {
|
||||||
val url = "$GEODE_API_BASE/loader/versions/latest".toHttpUrlOrNull()!!
|
val url = "$GEODE_API_BASE/loader/versions/latest".toHttpUrlOrNull()!!
|
||||||
.newBuilder()
|
.newBuilder()
|
||||||
.addQueryParameter("gd", gd)
|
.addQueryParameter("gd", gd.toString())
|
||||||
.addQueryParameter("platform", "android")
|
.addQueryParameter("platform", "android")
|
||||||
.addQueryParameter("prerelease", prerelease.toString())
|
.addQueryParameter("prerelease", prerelease.toString())
|
||||||
.build()
|
.build()
|
||||||
|
|||||||
@@ -21,7 +21,9 @@ import androidx.activity.result.ActivityResultLauncher
|
|||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.annotation.Keep
|
import androidx.annotation.Keep
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.browser.customtabs.CustomTabsIntent
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.net.toUri
|
||||||
import androidx.documentfile.provider.DocumentFile
|
import androidx.documentfile.provider.DocumentFile
|
||||||
import com.geode.launcher.BuildConfig
|
import com.geode.launcher.BuildConfig
|
||||||
import com.geode.launcher.R
|
import com.geode.launcher.R
|
||||||
@@ -29,6 +31,7 @@ import com.geode.launcher.UserDirectoryProvider
|
|||||||
import com.geode.launcher.activityresult.GeodeOpenFileActivityResult
|
import com.geode.launcher.activityresult.GeodeOpenFileActivityResult
|
||||||
import com.geode.launcher.activityresult.GeodeOpenFilesActivityResult
|
import com.geode.launcher.activityresult.GeodeOpenFilesActivityResult
|
||||||
import com.geode.launcher.activityresult.GeodeSaveFileActivityResult
|
import com.geode.launcher.activityresult.GeodeSaveFileActivityResult
|
||||||
|
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.lang.ref.WeakReference
|
import java.lang.ref.WeakReference
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
@@ -37,6 +40,8 @@ import kotlin.system.exitProcess
|
|||||||
@Suppress("unused", "KotlinJniMissingFunction")
|
@Suppress("unused", "KotlinJniMissingFunction")
|
||||||
object GeodeUtils {
|
object GeodeUtils {
|
||||||
private lateinit var activity: WeakReference<AppCompatActivity>
|
private lateinit var activity: WeakReference<AppCompatActivity>
|
||||||
|
var handleSafeArea: Boolean = false
|
||||||
|
|
||||||
private lateinit var openFileResultLauncher: ActivityResultLauncher<GeodeOpenFileActivityResult.OpenFileParams>
|
private lateinit var openFileResultLauncher: ActivityResultLauncher<GeodeOpenFileActivityResult.OpenFileParams>
|
||||||
private lateinit var openDirectoryResultLauncher: ActivityResultLauncher<Uri?>
|
private lateinit var openDirectoryResultLauncher: ActivityResultLauncher<Uri?>
|
||||||
private lateinit var openFilesResultLauncher: ActivityResultLauncher<GeodeOpenFilesActivityResult.OpenFileParams>
|
private lateinit var openFilesResultLauncher: ActivityResultLauncher<GeodeOpenFilesActivityResult.OpenFileParams>
|
||||||
@@ -386,6 +391,9 @@ object GeodeUtils {
|
|||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the current version code of the launcher.
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun getLauncherVersion(): Int {
|
fun getLauncherVersion(): Int {
|
||||||
return BuildConfig.VERSION_CODE
|
return BuildConfig.VERSION_CODE
|
||||||
@@ -457,9 +465,16 @@ object GeodeUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// vibration
|
// vibration
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If vibration is supported on the device.
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun vibrateSupported(): Boolean = getVibrator()?.hasVibrator() == true
|
fun vibrateSupported(): Boolean = getVibrator()?.hasVibrator() == true
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vibrates for the given amount of time.
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun vibrate(ms: Long) {
|
fun vibrate(ms: Long) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
@@ -470,6 +485,10 @@ object GeodeUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vibrates with the given pattern.
|
||||||
|
* @see android.os.VibrationEffect.createWaveform
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun vibratePattern(pattern: LongArray, repeat: Int) {
|
fun vibratePattern(pattern: LongArray, repeat: Int) {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
@@ -480,6 +499,19 @@ object GeodeUtils {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getScreenInsets(): IntArray? {
|
||||||
|
if (!handleSafeArea || Build.VERSION.SDK_INT < Build.VERSION_CODES.P) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return activity.get()?.run {
|
||||||
|
val displayCutout = window.decorView.rootWindowInsets?.displayCutout ?: return null
|
||||||
|
|
||||||
|
intArrayOf(displayCutout.safeInsetLeft, displayCutout.safeInsetBottom, displayCutout.safeInsetRight, displayCutout.safeInsetTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const val ARGUMENT_SAFE_MODE = "--geode:safe-mode"
|
const val ARGUMENT_SAFE_MODE = "--geode:safe-mode"
|
||||||
|
|
||||||
private var additionalLaunchArguments = arrayListOf<String>()
|
private var additionalLaunchArguments = arrayListOf<String>()
|
||||||
@@ -511,6 +543,7 @@ object GeodeUtils {
|
|||||||
|
|
||||||
const val CAPABILITY_EXTENDED_INPUT = "extended_input"
|
const val CAPABILITY_EXTENDED_INPUT = "extended_input"
|
||||||
const val CAPABILITY_TIMESTAMP_INPUT = "timestamp_inputs"
|
const val CAPABILITY_TIMESTAMP_INPUT = "timestamp_inputs"
|
||||||
|
const val CAPABILITY_INTERNAL_CALLBACKS = "internal_callbacks_v1"
|
||||||
|
|
||||||
private var capabilityListener: WeakReference<CapabilityListener?> = WeakReference(null)
|
private var capabilityListener: WeakReference<CapabilityListener?> = WeakReference(null)
|
||||||
|
|
||||||
@@ -518,6 +551,10 @@ object GeodeUtils {
|
|||||||
capabilityListener = WeakReference(listener)
|
capabilityListener = WeakReference(listener)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reports that the game client supports a feature.
|
||||||
|
* These features typically require callbacks into the game, so ensure that the required methods are registered first.
|
||||||
|
*/
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun reportPlatformCapability(capability: String?): Boolean {
|
fun reportPlatformCapability(capability: String?): Boolean {
|
||||||
if (capability.isNullOrEmpty()) {
|
if (capability.isNullOrEmpty()) {
|
||||||
@@ -527,11 +564,30 @@ object GeodeUtils {
|
|||||||
return capabilityListener.get()?.onCapabilityAdded(capability) == true
|
return capabilityListener.get()?.onCapabilityAdded(capability) == true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun openWebview(url: String) {
|
||||||
|
val activity = activity.get() ?: return
|
||||||
|
CustomTabsIntent.Builder()
|
||||||
|
.setShowTitle(true)
|
||||||
|
.build()
|
||||||
|
.launchUrl(activity, url.toUri())
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun openMultilineIMEKeyboard() {
|
||||||
|
Cocos2dxGLSurfaceView.openMultilineIMEKeyboard()
|
||||||
|
}
|
||||||
|
|
||||||
external fun nativeKeyUp(keyCode: Int, modifiers: Int)
|
external fun nativeKeyUp(keyCode: Int, modifiers: Int)
|
||||||
external fun nativeKeyDown(keyCode: Int, modifiers: Int, isRepeating: Boolean)
|
external fun nativeKeyDown(keyCode: Int, modifiers: Int, isRepeating: Boolean)
|
||||||
external fun nativeActionScroll(scrollX: Float, scrollY: Float)
|
external fun nativeActionScroll(scrollX: Float, scrollY: Float)
|
||||||
external fun resizeSurface(width: Int, height: Int)
|
external fun resizeSurface(width: Int, height: Int)
|
||||||
|
|
||||||
// represents the timestamp of the next input callback, in nanoseconds (most events don't send it, but it's there)
|
/**
|
||||||
|
* Gives the timestamp of the next input callback, in nanoseconds.
|
||||||
|
* Enable this by sending the "timestamp_inputs" platform capability.
|
||||||
|
* @see reportPlatformCapability
|
||||||
|
*/
|
||||||
external fun setNextInputTimestamp(timestamp: Long)
|
external fun setNextInputTimestamp(timestamp: Long)
|
||||||
|
external fun setNextInputTimestampInternal(timestamp: Long)
|
||||||
}
|
}
|
||||||
@@ -147,13 +147,15 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
|
|||||||
CUSTOM_ASPECT_RATIO,
|
CUSTOM_ASPECT_RATIO,
|
||||||
SCREEN_ZOOM,
|
SCREEN_ZOOM,
|
||||||
SCREEN_ZOOM_FIT,
|
SCREEN_ZOOM_FIT,
|
||||||
LIMIT_FRAME_RATE
|
LIMIT_FRAME_RATE,
|
||||||
|
USE_INDEX_API,
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun defaultValueForBooleanKey(key: Key): Boolean {
|
private fun defaultValueForBooleanKey(key: Key): Boolean {
|
||||||
return when (key) {
|
return when (key) {
|
||||||
Key.UPDATE_AUTOMATICALLY, Key.FORCE_HRR -> true
|
Key.UPDATE_AUTOMATICALLY, Key.FORCE_HRR -> true
|
||||||
Key.DEVELOPER_MODE -> BuildConfig.DEBUG
|
Key.DEVELOPER_MODE -> BuildConfig.DEBUG
|
||||||
|
Key.USE_INDEX_API -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,6 +197,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
|
|||||||
Key.SCREEN_ZOOM -> "PreferenceScreenZoom"
|
Key.SCREEN_ZOOM -> "PreferenceScreenZoom"
|
||||||
Key.SCREEN_ZOOM_FIT -> "PreferenceScreenZoomFit"
|
Key.SCREEN_ZOOM_FIT -> "PreferenceScreenZoomFit"
|
||||||
Key.LIMIT_FRAME_RATE -> "PreferenceLimitFrameRate"
|
Key.LIMIT_FRAME_RATE -> "PreferenceLimitFrameRate"
|
||||||
|
Key.USE_INDEX_API -> "PreferenceUseIndexApi"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +1,27 @@
|
|||||||
package org.cocos2dx.lib
|
package org.cocos2dx.lib
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.opengl.GLSurfaceView
|
import android.opengl.GLSurfaceView
|
||||||
import android.os.Build
|
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.util.Log
|
||||||
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.inputmethod.InputMethodManager
|
import android.view.inputmethod.InputMethodManager
|
||||||
import androidx.annotation.RequiresApi
|
import androidx.annotation.RequiresApi
|
||||||
|
import androidx.appcompat.widget.AppCompatEditText
|
||||||
import com.customRobTop.BaseRobTopActivity
|
import com.customRobTop.BaseRobTopActivity
|
||||||
import com.geode.launcher.utils.GeodeUtils
|
import com.geode.launcher.utils.GeodeUtils
|
||||||
|
|
||||||
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 HANDLER_ARG_MULTILINE = 1
|
||||||
|
private const val HANDLER_ARG_SINGLELINE = 0
|
||||||
private const val MS_TO_NS = 1_000_000
|
private const val MS_TO_NS = 1_000_000
|
||||||
|
|
||||||
class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
||||||
@@ -25,11 +30,22 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
private lateinit var handler: Handler
|
private lateinit var handler: Handler
|
||||||
private lateinit var cocos2dxTextInputWrapper: Cocos2dxTextInputWrapper
|
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
|
@JvmStatic
|
||||||
fun openIMEKeyboard() {
|
fun openIMEKeyboard() {
|
||||||
val msg = Message()
|
val msg = Message().apply {
|
||||||
msg.what = HANDLER_OPEN_IME_KEYBOARD
|
what = HANDLER_OPEN_IME_KEYBOARD
|
||||||
msg.obj = cocos2dxGLSurfaceView.getContentText()
|
obj = cocos2dxGLSurfaceView.getContentText()
|
||||||
|
arg1 = HANDLER_ARG_SINGLELINE
|
||||||
|
}
|
||||||
handler.sendMessage(msg)
|
handler.sendMessage(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,16 +70,29 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var sendTimestampEvents = false
|
var sendTimestampEvents = false
|
||||||
|
var sendInternalTimestampEvents = false
|
||||||
|
var manualBackEvents = false
|
||||||
|
|
||||||
var cocos2dxEditText: Cocos2dxEditText? = null
|
var cocos2dxEditText: AppCompatEditText? = null
|
||||||
set(value) {
|
set(value) {
|
||||||
field = value
|
field = value
|
||||||
|
|
||||||
field?.setOnEditorActionListener(cocos2dxTextInputWrapper)
|
field?.setOnEditorActionListener(cocos2dxTextInputWrapper)
|
||||||
field?.cocos2dxGLSurfaceView = this
|
|
||||||
requestFocus()
|
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() {
|
fun initView() {
|
||||||
setEGLContextClientVersion(2)
|
setEGLContextClientVersion(2)
|
||||||
isFocusableInTouchMode = true
|
isFocusableInTouchMode = true
|
||||||
@@ -75,6 +104,12 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
when (msg.what) {
|
when (msg.what) {
|
||||||
HANDLER_OPEN_IME_KEYBOARD -> {
|
HANDLER_OPEN_IME_KEYBOARD -> {
|
||||||
if (cocos2dxEditText?.requestFocus() == true) {
|
if (cocos2dxEditText?.requestFocus() == true) {
|
||||||
|
if (msg.arg1 == HANDLER_ARG_MULTILINE) {
|
||||||
|
setKeyboardMultiline()
|
||||||
|
} else {
|
||||||
|
setKeyboardSingleLine()
|
||||||
|
}
|
||||||
|
|
||||||
cocos2dxEditText?.apply {
|
cocos2dxEditText?.apply {
|
||||||
removeTextChangedListener(
|
removeTextChangedListener(
|
||||||
cocos2dxTextInputWrapper
|
cocos2dxTextInputWrapper
|
||||||
@@ -119,8 +154,21 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@RequiresApi(Build.VERSION_CODES.R)
|
@RequiresApi(Build.VERSION_CODES.R)
|
||||||
fun updateRefreshRate(refreshRate: Float) {
|
fun updateRefreshRate() {
|
||||||
holder.surface.setFrameRate(refreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
|
val chosenDisplay = display.supportedModes?.maxByOrNull { it.refreshRate }
|
||||||
|
if (chosenDisplay == null) {
|
||||||
|
println("updateRefreshRate failed to find a display to maximize refresh rate...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val chosenRefreshRate = chosenDisplay.refreshRate
|
||||||
|
|
||||||
|
println("updateRefreshRate: selecting refresh rate of ${chosenDisplay.refreshRate} (display ${chosenDisplay.modeId})")
|
||||||
|
|
||||||
|
holder.surface.setFrameRate(chosenRefreshRate, Surface.FRAME_RATE_COMPATIBILITY_DEFAULT)
|
||||||
|
if (isAttachedToWindow) {
|
||||||
|
(context as Activity).window.attributes.preferredRefreshRate = chosenRefreshRate
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPause() {
|
override fun onPause() {
|
||||||
@@ -135,6 +183,14 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
queueEvent { cocos2dxRenderer.handleOnResume() }
|
queueEvent { cocos2dxRenderer.handleOnResume() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun sendNextEventTimestamp(timestamp: Long) {
|
||||||
|
if (sendTimestampEvents)
|
||||||
|
GeodeUtils.setNextInputTimestamp(timestamp)
|
||||||
|
|
||||||
|
if (sendInternalTimestampEvents)
|
||||||
|
GeodeUtils.setNextInputTimestampInternal(timestamp)
|
||||||
|
}
|
||||||
|
|
||||||
override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
|
override fun onTouchEvent(motionEvent: MotionEvent): Boolean {
|
||||||
val pointerNumber = motionEvent.pointerCount
|
val pointerNumber = motionEvent.pointerCount
|
||||||
val ids = IntArray(pointerNumber)
|
val ids = IntArray(pointerNumber)
|
||||||
@@ -154,9 +210,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
val xDown = xs[0]
|
val xDown = xs[0]
|
||||||
val f = ys[0]
|
val f = ys[0]
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(timestamp)
|
||||||
GeodeUtils.setNextInputTimestamp(timestamp)
|
|
||||||
|
|
||||||
cocos2dxRenderer.handleActionDown(idDown, xDown, f)
|
cocos2dxRenderer.handleActionDown(idDown, xDown, f)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -166,27 +220,21 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
val f2 = xs[0]
|
val f2 = xs[0]
|
||||||
val f3 = ys[0]
|
val f3 = ys[0]
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(timestamp)
|
||||||
GeodeUtils.setNextInputTimestamp(timestamp)
|
|
||||||
|
|
||||||
cocos2dxRenderer.handleActionUp(idUp, f2, f3)
|
cocos2dxRenderer.handleActionUp(idUp, f2, f3)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_MOVE -> {
|
MotionEvent.ACTION_MOVE -> {
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(timestamp)
|
||||||
GeodeUtils.setNextInputTimestamp(timestamp)
|
|
||||||
|
|
||||||
cocos2dxRenderer.handleActionMove(ids, xs, ys)
|
cocos2dxRenderer.handleActionMove(ids, xs, ys)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_CANCEL -> {
|
MotionEvent.ACTION_CANCEL -> {
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(timestamp)
|
||||||
GeodeUtils.setNextInputTimestamp(timestamp)
|
|
||||||
|
|
||||||
cocos2dxRenderer.handleActionCancel(ids, xs, ys)
|
cocos2dxRenderer.handleActionCancel(ids, xs, ys)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -197,9 +245,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
val xPointerDown = motionEvent.getX(indexPointerDown)
|
val xPointerDown = motionEvent.getX(indexPointerDown)
|
||||||
val y = motionEvent.getY(indexPointerDown)
|
val y = motionEvent.getY(indexPointerDown)
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(timestamp)
|
||||||
GeodeUtils.setNextInputTimestamp(timestamp)
|
|
||||||
|
|
||||||
cocos2dxRenderer.handleActionDown(idPointerDown, xPointerDown, y)
|
cocos2dxRenderer.handleActionDown(idPointerDown, xPointerDown, y)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -210,9 +256,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
val xPointerUp = motionEvent.getX(indexPointUp)
|
val xPointerUp = motionEvent.getX(indexPointUp)
|
||||||
val y2 = motionEvent.getY(indexPointUp)
|
val y2 = motionEvent.getY(indexPointUp)
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(timestamp)
|
||||||
GeodeUtils.setNextInputTimestamp(timestamp)
|
|
||||||
|
|
||||||
cocos2dxRenderer.handleActionUp(idPointerUp, xPointerUp, y2)
|
cocos2dxRenderer.handleActionUp(idPointerUp, xPointerUp, y2)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -245,6 +289,28 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sendKeyBack() {
|
||||||
|
if (cocos2dxEditText?.isFocused == true) {
|
||||||
|
requestFocus()
|
||||||
|
}
|
||||||
|
|
||||||
|
if (BaseRobTopActivity.blockBackButton) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (useKeyboardEvents) {
|
||||||
|
queueEvent {
|
||||||
|
cocos2dxRenderer.handleKeyDown(KeyEvent.KEYCODE_BACK)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
val currentTime = System.nanoTime()
|
||||||
|
queueEvent {
|
||||||
|
sendNextEventTimestamp(currentTime)
|
||||||
|
GeodeUtils.nativeKeyDown(KeyEvent.KEYCODE_BACK, 0, false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
||||||
if (!useKeyboardEvents) {
|
if (!useKeyboardEvents) {
|
||||||
return legacyKeyDown(keyCode, event)
|
return legacyKeyDown(keyCode, event)
|
||||||
@@ -261,9 +327,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(event.eventTime * MS_TO_NS)
|
||||||
GeodeUtils.setNextInputTimestamp(event.eventTime * MS_TO_NS)
|
|
||||||
|
|
||||||
GeodeUtils.nativeKeyDown(keyCode, event.modifiers, event.repeatCount != 0)
|
GeodeUtils.nativeKeyDown(keyCode, event.modifiers, event.repeatCount != 0)
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
@@ -286,9 +350,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(event.eventTime * MS_TO_NS)
|
||||||
GeodeUtils.setNextInputTimestamp(event.eventTime * MS_TO_NS)
|
|
||||||
|
|
||||||
GeodeUtils.nativeKeyUp(keyCode, event.modifiers)
|
GeodeUtils.nativeKeyUp(keyCode, event.modifiers)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -309,9 +371,7 @@ class Cocos2dxGLSurfaceView(context: Context) : GLSurfaceView(context) {
|
|||||||
event.eventTimeNanos else event.eventTime * MS_TO_NS
|
event.eventTimeNanos else event.eventTime * MS_TO_NS
|
||||||
|
|
||||||
queueEvent {
|
queueEvent {
|
||||||
if (sendTimestampEvents)
|
sendNextEventTimestamp(timestamp)
|
||||||
GeodeUtils.setNextInputTimestamp(timestamp)
|
|
||||||
|
|
||||||
GeodeUtils.nativeActionScroll(scrollX, scrollY)
|
GeodeUtils.nativeActionScroll(scrollX, scrollY)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,7 +64,7 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceVi
|
|||||||
private var screenWidth = 0
|
private var screenWidth = 0
|
||||||
private var screenHeight = 0
|
private var screenHeight = 0
|
||||||
var sendResizeEvents = false
|
var sendResizeEvents = false
|
||||||
var setFrameRate: Float? = null
|
var setFrameRate = false
|
||||||
private var mAnimationInterval: Long? = null
|
private var mAnimationInterval: Long? = null
|
||||||
|
|
||||||
fun limitFrameRate(rate: Int) {
|
fun limitFrameRate(rate: Int) {
|
||||||
@@ -82,9 +82,8 @@ class Cocos2dxRenderer(private var handler: Cocos2dxGLSurfaceView) : GLSurfaceVi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onSurfaceChanged(gl10: GL10?, width: Int, height: Int) {
|
override fun onSurfaceChanged(gl10: GL10?, width: Int, height: Int) {
|
||||||
val chosenRefreshRate = setFrameRate
|
if (setFrameRate && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
if (chosenRefreshRate != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
handler.updateRefreshRate()
|
||||||
handler.updateRefreshRate(chosenRefreshRate)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
println("renderer surfaceChanged: ${width}x${height}@${handler.display.refreshRate}fps")
|
println("renderer surfaceChanged: ${width}x${height}@${handler.display.refreshRate}fps")
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ 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
|
||||||
|
|
||||||
@@ -47,22 +48,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 {
|
||||||
|
if (actionID != EditorInfo.IME_ACTION_DONE) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if (cocos2dxGLSurfaceView.cocos2dxEditText == textView && isFullScreenEdit) {
|
if (cocos2dxGLSurfaceView.cocos2dxEditText == textView && isFullScreenEdit) {
|
||||||
for (i in originText!!.length downTo 1) {
|
for (i in originText!!.length downTo 1) {
|
||||||
cocos2dxGLSurfaceView.deleteBackward()
|
cocos2dxGLSurfaceView.deleteBackward()
|
||||||
}
|
}
|
||||||
var text = textView.text.toString()
|
var text = textView.text.toString()
|
||||||
if (text.compareTo("") == 0) {
|
if (!text.endsWith('\n')) {
|
||||||
text = "\n"
|
text += '\n'
|
||||||
}
|
|
||||||
if (10 != text[text.length - 1].code) {
|
|
||||||
text += 10.toChar()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cocos2dxGLSurfaceView.insertText(text)
|
cocos2dxGLSurfaceView.insertText(text)
|
||||||
}
|
}
|
||||||
if (actionID != 6) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
cocos2dxGLSurfaceView.requestFocus()
|
cocos2dxGLSurfaceView.requestFocus()
|
||||||
Cocos2dxGLSurfaceView.closeIMEKeyboard()
|
Cocos2dxGLSurfaceView.closeIMEKeyboard()
|
||||||
return false
|
return false
|
||||||
|
|||||||
@@ -84,6 +84,7 @@
|
|||||||
<string name="release_fetch_in_progress">Checking for new releases…</string>
|
<string name="release_fetch_in_progress">Checking for new releases…</string>
|
||||||
<string name="release_fetch_success">Geode updated!</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">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_cancelling">Cancelling update…</string>
|
||||||
<string name="release_fetch_no_internet">No internet found.</string>
|
<string name="release_fetch_no_internet">No internet found.</string>
|
||||||
<string name="release_fetch_cancelled">Update cancelled.</string>
|
<string name="release_fetch_cancelled">Update cancelled.</string>
|
||||||
@@ -254,4 +255,5 @@
|
|||||||
|
|
||||||
<string name="preference_override_exceptions_name">Custom exceptions fix symbols</string>
|
<string name="preference_override_exceptions_name">Custom exceptions fix symbols</string>
|
||||||
<string name="preference_override_exceptions_description">Uses exception_symbols.txt to patch</string>
|
<string name="preference_override_exceptions_description">Uses exception_symbols.txt to patch</string>
|
||||||
|
<string name="preference_use_index_update_api">Use index release API</string>
|
||||||
</resources>
|
</resources>
|
||||||
+6
-6
@@ -1,14 +1,14 @@
|
|||||||
buildscript {
|
buildscript {
|
||||||
val composeBOM by extra("2025.01.00")
|
val composeBOM by extra("2025.04.01")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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 "8.8.0" apply false
|
id("com.android.application") version "8.9.2" apply false
|
||||||
id("com.android.library") version "8.8.0" apply false
|
id("com.android.library") version "8.9.2" apply false
|
||||||
id("org.jetbrains.kotlin.android") version "2.1.0" apply false
|
id("org.jetbrains.kotlin.android") version "2.1.20" apply false
|
||||||
id("org.jetbrains.kotlin.plugin.serialization") version "2.1.0" apply false
|
id("org.jetbrains.kotlin.plugin.serialization") version "2.1.20" apply false
|
||||||
id("org.jetbrains.kotlin.plugin.compose") version "2.1.0" apply false
|
id("org.jetbrains.kotlin.plugin.compose") version "2.1.20" apply false
|
||||||
}
|
}
|
||||||
|
|
||||||
tasks.register<Delete>("clean") {
|
tasks.register<Delete>("clean") {
|
||||||
|
|||||||
+2
-1
@@ -1,6 +1,7 @@
|
|||||||
|
#Thu Apr 24 10:54:01 MST 2025
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
|
||||||
networkTimeout=10000
|
networkTimeout=10000
|
||||||
validateDistributionUrl=true
|
validateDistributionUrl=true
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
Reference in new issue
Block a user