Bundle Geode with Play Store build (#88)

* add gradle task to download geode binaries

* disable automatic update on play build

* get download url from release api

* fix incorrect priorities between external library and prebundled

* bundle geode resources
this also fixes the release api fetch as i never tested it until now, oops

* add message for loading from files dir

* raise minimum gd version with bundled geode

* clear old resources prior to copying
This commit is contained in:
Chloe authored and GitHub committed 2026-09-07 17:19:12 -07:00
1 parent 9e86bbe355
commit 3194dff7ff
14 files changed
+455 -83

No files matched your search

+247
View File
@@ -1,3 +1,9 @@
import groovy.json.JsonSlurper
import java.io.IOException
import java.net.URI
import java.security.MessageDigest
import java.util.zip.ZipInputStream
kotlin {
compilerOptions {
extraWarnings.set(true)
@@ -10,6 +16,232 @@ plugins {
id("org.jetbrains.kotlin.plugin.compose")
}
val currentGeodeVersion = "5.10.1"
abstract class DownloadVersionMetadataTask : DefaultTask() {
@get:Input
abstract val geodeVersion: Property<String>
@get:OutputFile
abstract val targetFile: RegularFileProperty
@TaskAction
fun execute() {
val downloadGeodeVersion = geodeVersion.get()
val destinationFile = targetFile.get().asFile
val releaseUrl = "https://api.geode-sdk.org/v1/loader/versions/$downloadGeodeVersion"
URI.create(releaseUrl).toURL().openStream().use { response ->
destinationFile.outputStream().use { destination ->
response.copyTo(destination)
}
}
}
}
abstract class DownloadGeodeFileTask : DefaultTask() {
@get:Input
abstract val platform: Property<String>
@get:InputFile
abstract val metadata: RegularFileProperty
private fun downloadAndVerifyFile(filename: String, url: String, hash: String?): File {
logger.info("Downloading $filename from $url")
val tempFile = File(temporaryDir, filename)
URI.create(url).toURL().openStream().use { response ->
tempFile.outputStream().use { output ->
response.copyTo(output)
}
}
if (hash != null) {
val downloadedHash = MessageDigest.getInstance("SHA-256")
.digest(tempFile.readBytes())
.toHexString()
if (downloadedHash != hash) {
throw GradleException("Hash check failed for file $filename, expected $hash but found $downloadedHash.")
}
}
return tempFile
}
fun downloadZip(fallbackUrl: String?): File {
val downloadPlatform = platform.get()
val metadataFile = metadata.get().asFile
val json = JsonSlurper().parse(metadataFile) as Map<*, *>
val payload = json["payload"] as? Map<*, *>
val downloads = payload?.get("downloads") as? Map<*, *>
val platformInfo = downloads?.get(downloadPlatform) as? Map<*, *>
val downloadUrl = platformInfo?.get("url") as? String ?: fallbackUrl
if (downloadUrl == null) {
throw GradleException("Failed to fetch download url for platform $platform.")
}
val expectedHash = platformInfo?.get("hash") as? String
return downloadAndVerifyFile("$downloadPlatform.zip", downloadUrl, expectedHash)
}
}
abstract class DownloadAndUnzipGeodeTask : DownloadGeodeFileTask() {
@get:Input
abstract val geodeVersion: Property<String>
@get:OutputFile
abstract val targetFile: RegularFileProperty
@TaskAction
fun execute() {
val downloadGeodeVersion = geodeVersion.get()
val downloadPlatform = platform.get()
val destinationFile = targetFile.get().asFile
val tempOutput = this.downloadZip("https://github.com/geode-sdk/geode/releases/download/v$downloadGeodeVersion/geode-v$downloadGeodeVersion-$downloadPlatform.zip")
val expectedFile = "Geode.$downloadPlatform.so"
ZipInputStream(tempOutput.inputStream()).use { zip ->
var entry = zip.nextEntry
while (entry != null) {
if (entry.name == expectedFile) {
destinationFile.outputStream().use {
zip.copyTo(it)
}
return@execute
}
entry = zip.nextEntry
}
throw GradleException("Could not find Geode release.")
}
}
}
abstract class DownloadAndUnzipGeodeResourcesTask : DownloadGeodeFileTask() {
init {
platform.set("resources")
}
@get:Input
abstract val geodeVersion: Property<String>
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction
fun execute() {
val downloadGeodeVersion = geodeVersion.get()
val tempOutput = this.downloadZip("https://github.com/geode-sdk/geode/releases/download/v$downloadGeodeVersion/resources.zip")
val baseOutput = outputDir.get().asFile
if (baseOutput.exists()) baseOutput.deleteRecursively()
val output = File(baseOutput, "geode.loader")
output.mkdirs()
val canonicalOutput = output.canonicalPath
ZipInputStream(tempOutput.inputStream()).use { zipStream ->
var entry = zipStream.nextEntry
while (entry != null) {
val destination = File(output, entry.name)
if (!destination.canonicalPath.startsWith(canonicalOutput)) {
throw GradleException("attempted copy to outside of output directory: ${entry.name}")
}
if (destination.isDirectory) {
destination.mkdirs()
} else {
destination.parentFile?.mkdirs()
destination.outputStream().use { destinationStream ->
zipStream.copyTo(destinationStream)
}
}
entry = zipStream.nextEntry
}
}
}
}
val fetchGeodeMetadataTask = tasks.register<DownloadVersionMetadataTask>("fetchGeodeMetadata") {
description = "Fetches and saves a Geode version's metadata"
inputs.property("geodeVersion", currentGeodeVersion)
geodeVersion = currentGeodeVersion
targetFile = temporaryDir.resolve("metadata.json")
}
val downloadGeode32Task = tasks.register<DownloadAndUnzipGeodeTask>("downloadGeode32Binary") {
description = "Downloads the 32bit Geode release archive"
inputs.property("geodeVersion", currentGeodeVersion)
geodeVersion = currentGeodeVersion
platform = "android32"
metadata = fetchGeodeMetadataTask.flatMap { it.targetFile }
targetFile = temporaryDir.resolve("libgeode.so")
}
val downloadGeode64Task = tasks.register<DownloadAndUnzipGeodeTask>("downloadGeode64Binary") {
description = "Downloads the 64bit Geode release archive"
inputs.property("geodeVersion", currentGeodeVersion)
geodeVersion = currentGeodeVersion
platform = "android64"
metadata = fetchGeodeMetadataTask.flatMap { it.targetFile }
targetFile = temporaryDir.resolve("libgeode.so")
}
val downloadGeodeResourcesTask = tasks.register<DownloadAndUnzipGeodeResourcesTask>("downloadGeodeResources") {
description = "Downloads Geode resources"
inputs.property("geodeVersion", currentGeodeVersion)
geodeVersion = currentGeodeVersion
metadata = fetchGeodeMetadataTask.flatMap { it.targetFile }
outputDir = layout.buildDirectory.dir("geode-resources")
}
abstract class MergeGeodeFilesTask : DefaultTask() {
@get:InputFile
abstract val binary32: RegularFileProperty
@get:InputFile
abstract val binary64: RegularFileProperty
@get:OutputDirectory
abstract val outputDir: DirectoryProperty
@TaskAction
fun execute() {
val out = outputDir.get().asFile
out.deleteRecursively()
out.mkdirs()
binary32.get().asFile.copyTo(outputDir.file("armeabi-v7a/libgeode.so").get().asFile)
binary64.get().asFile.copyTo(outputDir.file("arm64-v8a/libgeode.so").get().asFile)
}
}
val fullDownloadTask = tasks.register<MergeGeodeFilesTask>("downloadGeodeFull") {
description = "Downloads both Geode releases"
binary32 = downloadGeode32Task.flatMap { it.targetFile }
binary64 = downloadGeode64Task.flatMap { it.targetFile }
outputDir = layout.buildDirectory.dir("geode-prebuilt")
}
android {
compileSdk = 37
@@ -54,12 +286,14 @@ android {
dimension = "distribution"
isDefault = true
buildConfigField("boolean", "GOOGLE_PLAY_BUILD", "false")
buildConfigField("String", "PREBUNDLED_GEODE", "null")
}
create("googlePlay") {
dimension = "distribution"
applicationId = "com.geode.launcher.play"
buildConfigField("boolean", "GOOGLE_PLAY_BUILD", "true")
buildConfigField("String", "PREBUNDLED_GEODE", "\"$currentGeodeVersion\"")
}
}
@@ -100,6 +334,19 @@ android {
ndkVersion = "29.0.14206865"
}
androidComponents {
onVariants(selector().withFlavor("distribution" to "googlePlay")) { variant ->
variant.sources.jniLibs?.addGeneratedSourceDirectory(
fullDownloadTask,
MergeGeodeFilesTask::outputDir
)
variant.sources.assets?.addGeneratedSourceDirectory(
downloadGeodeResourcesTask,
DownloadAndUnzipGeodeResourcesTask::outputDir
)
}
}
dependencies {
implementation (platform("androidx.compose:compose-bom:2026.08.00"))
implementation ("androidx.core:core-ktx:1.19.0")
@@ -379,56 +379,73 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
return "$sourceDir!/lib/$architecture/"
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
private fun loadGeodeLibrary() {
// Load Geode if exists
// bundling the object with the application allows for nicer backtraces
try {
// put libgeode.so in jniLibs/armeabi-v7a to get this
System.loadLibrary("geode")
return
} catch (e: UnsatisfiedLinkError) {
// but users may prefer it stored with data
val geodeFilename = LaunchUtils.geodeFilename
val geodePath = File(filesDir.path, "launcher/$geodeFilename")
if (geodePath.exists()) {
System.load(geodePath.path)
@Suppress("SENSELESS_COMPARISON")
if (BuildConfig.PREBUNDLED_GEODE != null) {
// attempt to load an externally provided Geode first
if (!loadExternalGeode()) {
copyGeodeResources()
System.loadLibrary("geode")
}
} else {
// Load Geode if exists
// bundling the object with the application allows for nicer backtraces
try {
// put libgeode.so in jniLibs/armeabi-v7a to get this
System.loadLibrary("geode")
return
}
// you know zmx i have 0 clue what this does so im
// just gonna like copy the binary from external
// also i get 20 million permission denied errors
val externalGeodePath = LaunchUtils.getInstalledGeodePath(this)!!
val copiedPath = File(filesDir.path, "copied")
if (copiedPath.exists()) {
copiedPath.deleteRecursively()
}
copiedPath.mkdir()
val copiedGeodePath = File(copiedPath.path, "Geode.so")
if (externalGeodePath.exists()) {
DownloadUtils.copyFile(
FileInputStream(externalGeodePath),
FileOutputStream(copiedGeodePath)
)
if (copiedGeodePath.exists()) {
// android 17 fix
copiedGeodePath.setWritable(false)
println("Loading Geode from ${externalGeodePath.name}")
System.load(copiedGeodePath.path)
return
} catch (e: UnsatisfiedLinkError) {
if (!loadExternalGeode()) {
throw e
}
}
throw e
}
}
@SuppressLint("UnsafeDynamicallyLoadedCode")
private fun loadExternalGeode(): Boolean {
// but users may prefer it stored with data
val geodeFilename = LaunchUtils.geodeFilename
val geodePath = File(filesDir.path, "launcher/$geodeFilename")
if (geodePath.exists()) {
println("Loading Geode from ${geodePath.path}")
System.load(geodePath.path)
return true
}
// you know zmx i have 0 clue what this does so im
// just gonna like copy the binary from external
// also i get 20 million permission denied errors
val externalGeodePath = LaunchUtils.getInstalledGeodePath(this)
?: return false
val copiedPath = File(filesDir.path, "copied")
if (copiedPath.exists()) {
copiedPath.deleteRecursively()
}
copiedPath.mkdir()
val copiedGeodePath = File(copiedPath.path, "Geode.so")
if (externalGeodePath.exists()) {
DownloadUtils.copyFile(
FileInputStream(externalGeodePath),
FileOutputStream(copiedGeodePath)
)
if (copiedGeodePath.exists()) {
// android 17 fix
copiedGeodePath.setWritable(false)
println("Loading Geode from ${externalGeodePath.name}")
System.load(copiedGeodePath.path)
return true
}
}
return false
}
private fun createView(): FrameLayout {
val frameLayoutParams = ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
@@ -666,6 +683,56 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
}
}
private fun copyGeodeResources() {
@Suppress("SENSELESS_COMPARISON")
if (BuildConfig.PREBUNDLED_GEODE == null) {
return
}
val resourcesDirectory = LaunchUtils.getGeodeResourcesDirectory(this)
val pathBase = "geode.loader"
val versionFilename = "version"
val bundledVersion = try {
assets.open("$pathBase/$versionFilename").bufferedReader().use {
it.readText()
}.trimEnd()
} catch (_: IOException) {
return
}
val versionFile = File(resourcesDirectory, versionFilename)
val resourcesVersion = if (versionFile.exists()) {
versionFile.readText().trimEnd()
} else null
if (bundledVersion == resourcesVersion) {
println("copyGeodeResources: found $resourcesVersion, expected $bundledVersion. No work needs to be done!")
return
}
if (resourcesDirectory.exists()) {
resourcesDirectory.deleteRecursively()
}
resourcesDirectory.mkdirs()
println("copyGeodeResources: found $resourcesVersion, expected $bundledVersion. Copying assets")
val fileListing = try {
assets.list(pathBase)
} catch (_: IOException) {
return
}
fileListing?.forEach { fileName ->
val resourceOutput = File(resourcesDirectory, fileName)
val resource = assets.open("$pathBase/$fileName")
DownloadUtils.copyFile(resource, resourceOutput.outputStream())
}
}
class EGLConfigChooser : GLSurfaceView.EGLConfigChooser {
// this comes from EGL14, but is unavailable on EGL10
// also EGL14 is incompatible with EGL10. so whatever
@@ -111,6 +111,16 @@ class MainActivity : ComponentActivity() {
enableEdgeToEdge()
clearDownloadedApks(this)
// remove old Geode for the like 4 people that downloaded the Play Store build
@Suppress("SENSELESS_COMPARISON")
if (BuildConfig.PREBUNDLED_GEODE != null) {
val preferences = PreferenceUtils.get(this)
if (!preferences.getBoolean(PreferenceUtils.Key.PREBUNDLED_GEODE_MIGRATION_PERFORMED)) {
LaunchUtils.getInstalledGeodePath(this)?.delete()
preferences.setBoolean(PreferenceUtils.Key.PREBUNDLED_GEODE_MIGRATION_PERFORMED, true)
}
}
val returnMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_MESSAGE)
val returnExtendedMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE)
@@ -207,7 +217,7 @@ fun mapCancelReasonToInfo(cancelReason: LaunchViewModel.LaunchCancelReason): Lau
)
LaunchViewModel.LaunchCancelReason.GAME_OUTDATED -> LaunchStatusInfo(
title = stringResource(R.string.launcher_cancelled_error),
details = stringResource(R.string.launcher_cancelled_outdated)
details = stringResource(R.string.launcher_cancelled_outdated, GamePackageUtils.getSupportedUnifiedVersion())
)
LaunchViewModel.LaunchCancelReason.AUTOMATIC,
LaunchViewModel.LaunchCancelReason.MANUAL-> LaunchStatusInfo(
@@ -828,10 +838,11 @@ fun AltMainScreen(
)
// only show launcher update in a case where a user won't see it ingame
if (launchUIState is LaunchViewModel.LaunchUIState.Cancelled) {
val currentUIState = launchUIState
if (currentUIState is LaunchViewModel.LaunchUIState.Cancelled) {
val gameVersion = remember { GamePackageUtils.getGameVersionCodeOrNull(context.packageManager) }
if (gameVersion != null && gameVersion < Constants.SUPPORTED_VERSION_CODE_MIN_WARNING) {
if (gameVersion != null && gameVersion < Constants.SUPPORTED_VERSION_CODE_MIN_WARNING && currentUIState.reason != LaunchViewModel.LaunchCancelReason.GAME_OUTDATED) {
UnsupportedVersionWarning()
}
@@ -202,7 +202,11 @@ fun onShowLogs(context: Context) {
fun ErrorInfoDescription(description: String, modifier: Modifier = Modifier) {
val context = LocalContext.current
val currentRelease = remember {
PreferenceUtils.get(context).getString(PreferenceUtils.Key.CURRENT_VERSION_TAG)
@Suppress("SENSELESS_COMPARISON")
if (BuildConfig.PREBUNDLED_GEODE != null)
"${BuildConfig.PREBUNDLED_GEODE}*"
else
PreferenceUtils.get(context).getString(PreferenceUtils.Key.CURRENT_VERSION_TAG)
}
val gameVersion = remember {
@@ -187,6 +187,8 @@ fun LongPressButton(onClick: () -> Unit, onLongPress: () -> Unit, modifier: Modi
@Composable
fun UnsupportedVersionWarning(modifier: Modifier = Modifier) {
val gameVersion = GamePackageUtils.getSupportedUnifiedVersion()
InlineDialog(
headline = {
Icon(
@@ -202,7 +204,7 @@ fun UnsupportedVersionWarning(modifier: Modifier = Modifier) {
},
body = {
Text(
stringResource(R.string.launcher_game_update_required),
stringResource(R.string.launcher_game_update_required, gameVersion),
)
},
modifier = modifier.padding(8.dp)
@@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.geode.launcher.BuildConfig
import com.geode.launcher.updater.ReleaseManager
import com.geode.launcher.utils.Constants
import com.geode.launcher.utils.GamePackageUtils
@@ -182,7 +183,9 @@ class LaunchViewModel(private val application: Application): ViewModel() {
return
}
if (GamePackageUtils.getGameVersionCode(packageManager) < Constants.SUPPORTED_VERSION_CODE_MIN) {
// play store build only comes with the one build of geode
val minVersionCode = if (BuildConfig.GOOGLE_PLAY_BUILD) Constants.SUPPORTED_VERSION_CODE else Constants.SUPPORTED_VERSION_CODE_MIN
if (GamePackageUtils.getGameVersionCode(packageManager) < minVersionCode) {
_uiState.tryEmit(LaunchUIState.Cancelled(LaunchCancelReason.GAME_OUTDATED))
return
}
@@ -248,25 +251,31 @@ class LaunchViewModel(private val application: Application): ViewModel() {
initReadyTimer()
}
val releaseManager = ReleaseManager.get(application)
if (!BuildConfig.GOOGLE_PLAY_BUILD) {
val releaseManager = ReleaseManager.get(application)
val hasGeode = LaunchUtils.isGeodeInstalled(application)
val shouldUpdate = PreferenceUtils.get(application).getBoolean(PreferenceUtils.Key.UPDATE_AUTOMATICALLY)
val hasGeode = LaunchUtils.isGeodeInstalled(application)
val shouldUpdate = PreferenceUtils.get(application).getBoolean(PreferenceUtils.Key.UPDATE_AUTOMATICALLY)
if ((shouldUpdate && !hasCheckedForUpdates) || !hasGeode) {
releaseManager.checkForUpdates(false)
} else {
releaseManager.afterUseCachedUpdate()
}
if ((shouldUpdate && !hasCheckedForUpdates) || !hasGeode) {
releaseManager.checkForUpdates(false)
} else {
releaseManager.afterUseCachedUpdate()
}
if (isRestart) {
releaseManager.dismissedLauncherUpdate = true
if (isRestart) {
releaseManager.dismissedLauncherUpdate = true
}
}
preReadyCheck()
}
fun retryUpdate() {
if (BuildConfig.GOOGLE_PLAY_BUILD) {
return
}
ReleaseManager.get(application).checkForUpdates(true)
if (ReleaseManager.get(application).isInUpdate) {
restoreUpdateState = _uiState.value
@@ -40,6 +40,7 @@ import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import com.geode.launcher.BuildConfig
import com.geode.launcher.R
import com.geode.launcher.preferences.components.OptionsButton
import com.geode.launcher.preferences.components.OptionsGroup
@@ -204,28 +205,30 @@ fun DeveloperSettingsScreen(onBackPressedDispatcher: OnBackPressedDispatcher?) {
)
}
OptionsGroup(title = stringResource(R.string.preference_category_updater)) {
val context = LocalContext.current
if (!BuildConfig.GOOGLE_PLAY_BUILD) {
OptionsGroup(title = stringResource(R.string.preference_category_updater)) {
val context = LocalContext.current
SettingsSelectCard(
title = stringResource(R.string.preference_release_channel_tag_name),
dialogTitle = stringResource(R.string.preference_release_channel_select),
preferenceKey = PreferenceUtils.Key.RELEASE_CHANNEL_TAG,
options = linkedMapOf(
0 to stringResource(R.string.preference_release_channel_stable),
1 to stringResource(R.string.preference_release_channel_beta),
2 to stringResource(R.string.preference_release_channel_nightly),
),
extraSelectBehavior = {
PreferenceUtils.get(context)
.setLong(PreferenceUtils.Key.LAST_UPDATE_CHECK_TIME, 0L)
}
)
SettingsSelectCard(
title = stringResource(R.string.preference_release_channel_tag_name),
dialogTitle = stringResource(R.string.preference_release_channel_select),
preferenceKey = PreferenceUtils.Key.RELEASE_CHANNEL_TAG,
options = linkedMapOf(
0 to stringResource(R.string.preference_release_channel_stable),
1 to stringResource(R.string.preference_release_channel_beta),
2 to stringResource(R.string.preference_release_channel_nightly),
),
extraSelectBehavior = {
PreferenceUtils.get(context)
.setLong(PreferenceUtils.Key.LAST_UPDATE_CHECK_TIME, 0L)
}
)
SettingsCard(
title = stringResource(R.string.preference_disable_update_cache),
preferenceKey = PreferenceUtils.Key.DISABLE_UPDATE_CACHE
)
SettingsCard(
title = stringResource(R.string.preference_disable_update_cache),
preferenceKey = PreferenceUtils.Key.DISABLE_UPDATE_CACHE
)
}
}
OptionsGroup(title = stringResource(R.string.preference_category_testing)) {
@@ -70,7 +70,6 @@ import androidx.compose.ui.unit.dp
import androidx.core.net.toUri
import androidx.lifecycle.viewmodel.compose.viewModel
import com.geode.launcher.BuildConfig
import com.geode.launcher.MainActivity
import com.geode.launcher.R
import com.geode.launcher.UserDirectoryProvider
import com.geode.launcher.preferences.components.BackupButton
@@ -510,6 +509,10 @@ fun GameplaySettingsGroup() {
@Composable
fun UpdateSettingsGroup(releaseViewModel: ReleaseViewModel, snackbarHostState: SnackbarHostState, onUpdate: () -> Unit) {
if (BuildConfig.GOOGLE_PLAY_BUILD) {
return
}
val updateStatus by releaseViewModel.uiState.collectAsState()
OptionsGroup(stringResource(R.string.preference_category_updater)) {
@@ -661,11 +664,14 @@ fun AboutSettingsGroup(developerModeEnabled: Boolean) {
)
}
@Suppress("SENSELESS_COMPARISON")
OptionsLabel(
stringResource(R.string.preference_loader_version_name),
stringResource(
R.string.preference_loader_version_description,
currentRelease ?: "unknown"
if (BuildConfig.PREBUNDLED_GEODE != null)
"${BuildConfig.PREBUNDLED_GEODE}*"
else (currentRelease ?: "unknown")
),
displayInline = true
)
@@ -409,6 +409,10 @@ class ReleaseManager private constructor(
}
fun shouldUseCache(): Boolean {
if (BuildConfig.GOOGLE_PLAY_BUILD) {
return true
}
val sharedPreferences = PreferenceUtils.get(applicationContext)
if (sharedPreferences.getBoolean(PreferenceUtils.Key.DISABLE_UPDATE_CACHE)) {
return false
@@ -427,6 +431,11 @@ class ReleaseManager private constructor(
}
private suspend fun checkForNewRelease(isManual: Boolean = false) {
if (BuildConfig.GOOGLE_PLAY_BUILD) {
_uiState.value = ReleaseManagerState.Finished()
return
}
val sharedPreferences = PreferenceUtils.get(applicationContext)
if (!isManual && shouldUseCache()) {
@@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.AP
import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import com.geode.launcher.BuildConfig
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
@@ -89,6 +90,10 @@ class ReleaseViewModel(private val application: Application): ViewModel() {
}
fun runReleaseCheck(isManual: Boolean = false) {
if (BuildConfig.GOOGLE_PLAY_BUILD) {
return
}
hasPerformedCheck = true
viewModelScope.launch(Dispatchers.IO) {
@@ -63,6 +63,10 @@ object GamePackageUtils {
return gameVersionMap[versionCode] ?: getGameVersionString(packageManager)
}
fun getSupportedUnifiedVersion(): String {
return gameVersionMap[Constants.SUPPORTED_VERSION_CODE] ?: Constants.SUPPORTED_VERSION_STRING
}
fun detectAbiMismatch(context: Context, packageInfo: PackageInfo, loadException: Error): Boolean {
val abi = LaunchUtils.applicationArchitecture
val isExtracted =
@@ -42,6 +42,9 @@ object LaunchUtils {
}
fun isGeodeInstalled(context: Context): Boolean {
@Suppress("SENSELESS_COMPARISON")
if (BuildConfig.PREBUNDLED_GEODE != null) return true
return getInstalledGeodePath(context) != null
}
@@ -149,6 +149,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
LAST_UPDATE_CHECK_TIME,
LAST_LAUNCHER_UPDATE,
DISABLE_UPDATE_CACHE,
PREBUNDLED_GEODE_MIGRATION_PERFORMED,
}
private fun defaultValueForBooleanKey(key: Key): Boolean {
@@ -199,6 +200,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
Key.LAST_UPDATE_CHECK_TIME -> "PreferenceLastUpdateCheckTime"
Key.LAST_LAUNCHER_UPDATE -> "PreferenceLastLauncherUpdate"
Key.DISABLE_UPDATE_CACHE -> "PreferenceDisableUpdateCache"
Key.PREBUNDLED_GEODE_MIGRATION_PERFORMED -> "PreferencePrebundledGeodeMigrationPerformed"
// Key.RELEASE_CHANNEL -> "PreferenceReleaseChannel"
// Key.LAST_DISMISSED_UPDATE -> "PreferenceLastDismissedUpdate"
// Key.FORCE_HRR -> "PreferenceForceHighRefreshRate"
+2 -2
View File
@@ -35,7 +35,7 @@
</string>
<string name="launcher_game_update_required_short">A Geometry Dash update is required.</string>
<string name="launcher_game_update_required">The currently installed version of Geometry Dash is no longer supported. Please update Geometry Dash to continue receiving Geode updates.</string>
<string name="launcher_game_update_required">The currently installed version of Geometry Dash is no longer supported. Please update Geometry Dash to version %1$s to continue receiving Geode updates.</string>
<string name="launcher_update_available">A new launcher update is available!</string>
<string name="launcher_update_dismiss">Dismiss</string>
@@ -82,7 +82,7 @@
<string name="launcher_cancelled_crash">Launch paused due to a crash.</string>
<string name="launcher_cancelled_crash_instant">Geode crashed.</string>
<string name="launcher_cancelled_error">Geode cannot be used.</string>
<string name="launcher_cancelled_outdated">A Geometry Dash update is required.</string>
<string name="launcher_cancelled_outdated">Geometry Dash version %1$s is required.</string>
<string name="launcher_cancelled_restart">Restart</string>
<string name="launcher_cancelled_resume">Resume</string>
<string name="launcher_cancelled_retry">Retry</string>