redo load failed popup

This commit is contained in:
qimiko committed 2024-02-12 23:33:00 -07:00
1 parent 9c7d87ff8a
commit 004526e685
7 files changed
+363 -173

No files matched your search

@@ -65,36 +65,39 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
val is64bit = LaunchUtils.is64bit
val errorMessage = when {
abiMismatch && is64bit -> getString(R.string.load_failed_abi_error_need_32bit_description)
abiMismatch -> getString(R.string.load_failed_abi_error_need_64bit_description)
else -> getString(R.string.load_failed_link_error_description)
abiMismatch && is64bit -> LaunchUtils.LauncherError.LINKER_NEEDS_32BIT
abiMismatch -> LaunchUtils.LauncherError.LINKER_NEEDS_64BIT
else -> LaunchUtils.LauncherError.LINKER_FAILED
}
returnToMain(
getString(R.string.load_failed_link_error),
errorMessage
)
returnToMain(errorMessage, e.message, e.stackTraceToString())
return
} catch (e: Exception) {
Log.e("GeodeLauncher", "Uncaught error during game load", e)
returnToMain(
getString(R.string.load_failed_generic_error),
getString(R.string.load_failed_generic_error_description, e.message ?: "UnknownException")
LaunchUtils.LauncherError.GENERIC,
e.message ?: "Unknown Exception",
e.stackTraceToString()
)
return
}
}
private fun returnToMain(returnTitle: String? = null, returnMessage: String? = null) {
private fun returnToMain(
error: LaunchUtils.LauncherError? = null,
returnMessage: String? = null,
returnExtendedMessage: String? = null
) {
val launchIntent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
if (!returnTitle.isNullOrEmpty() && !returnMessage.isNullOrEmpty()) {
putExtra(Constants.LAUNCHER_KEY_RETURN_TITLE, returnTitle)
putExtra(Constants.LAUNCHER_KEY_RETURN_MESSAGE, returnMessage)
if (error != null && !returnMessage.isNullOrEmpty()) {
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_ERROR, error)
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_MESSAGE, returnMessage)
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE, returnExtendedMessage)
}
}
@@ -30,6 +30,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalHapticFeedback
import androidx.compose.ui.platform.LocalUriHandler
@@ -37,6 +38,8 @@ import androidx.compose.ui.platform.LocalViewConfiguration
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@@ -58,6 +61,11 @@ import kotlinx.coroutines.flow.collectLatest
import java.net.ConnectException
import java.net.UnknownHostException
data class LoadFailureInfo(
val title: LaunchUtils.LauncherError,
val description: String,
val extendedMessage: String?
)
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
@@ -68,8 +76,12 @@ class MainActivity : ComponentActivity() {
val gdInstalled = LaunchUtils.isGeometryDashInstalled(packageManager)
val geodeInstalled = LaunchUtils.isGeodeInstalled(this)
val returnMessage = intent.extras?.getString(Constants.LAUNCHER_KEY_RETURN_MESSAGE)
val returnTitle = intent.extras?.getString(Constants.LAUNCHER_KEY_RETURN_TITLE)
val returnMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_MESSAGE)
val returnExtendedMessage = intent.extras?.getString(LaunchUtils.LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE)
@Suppress("DEPRECATION") // the new api is android 13 only (why)
val returnError = intent.extras?.getSerializable(LaunchUtils.LAUNCHER_KEY_RETURN_ERROR)
as? LaunchUtils.LauncherError
setContent {
val themeOption by PreferenceUtils.useIntPreference(PreferenceUtils.Key.THEME)
@@ -83,12 +95,11 @@ class MainActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
val loadFailed = !returnMessage.isNullOrEmpty() && !returnTitle.isNullOrEmpty()
MainScreen(gdInstalled, geodeInstalled, loadFailed)
val loadFailureInfo = if (returnError != null && returnMessage != null) {
LoadFailureInfo(returnError, returnMessage, returnExtendedMessage)
} else { null }
if (!returnMessage.isNullOrEmpty() && !returnTitle.isNullOrEmpty()) {
LoadFailedDialog(returnTitle, returnMessage)
}
MainScreen(gdInstalled, geodeInstalled, loadFailureInfo)
}
}
}
@@ -209,30 +220,6 @@ fun UpdateMessageIndicator(
}
@Composable
fun LoadFailedDialog(returnTitle: String, returnMessage: String) {
var showDialog by remember { mutableStateOf(true) }
if (showDialog) {
AlertDialog(
icon = {
Icon(
painterResource(R.drawable.icon_error),
contentDescription = stringResource(R.string.launcher_error_icon_alt)
)
},
title = { Text(returnTitle) },
text = { Text(returnMessage) },
confirmButton = {
TextButton(onClick = { showDialog = false }) {
Text(stringResource(R.string.message_box_accept))
}
},
onDismissRequest = { showDialog = false }
)
}
}
@Composable
fun UpdateWarning(inSafeMode: Boolean = false, onDismiss: () -> Unit) {
val context = LocalContext.current
@@ -422,6 +409,136 @@ fun UpdateCard(releaseViewModel: ReleaseViewModel, modifier: Modifier = Modifier
}
}
@Composable
fun ErrorInfoBody(failureReason: LaunchUtils.LauncherError, modifier: Modifier = Modifier) {
val headline = when (failureReason) {
LaunchUtils.LauncherError.LINKER_NEEDS_64BIT,
LaunchUtils.LauncherError.LINKER_NEEDS_32BIT -> stringResource(R.string.load_failed_abi_error)
LaunchUtils.LauncherError.LINKER_FAILED -> stringResource(R.string.load_failed_link_error_description)
LaunchUtils.LauncherError.GENERIC -> stringResource(R.string.load_failed_generic_error_description)
}
val recommendations = when (failureReason) {
LaunchUtils.LauncherError.LINKER_NEEDS_64BIT -> listOf(
stringResource(R.string.load_failed_recommendation_reinstall),
stringResource(R.string.load_failed_recommendation_switch_64bit_abi),
)
LaunchUtils.LauncherError.LINKER_NEEDS_32BIT -> listOf(
stringResource(R.string.load_failed_recommendation_reinstall),
stringResource(R.string.load_failed_recommendation_switch_32bit_abi),
)
LaunchUtils.LauncherError.LINKER_FAILED -> listOf(
stringResource(R.string.load_failed_recommendation_reinstall),
stringResource(R.string.load_failed_recommendation_update),
)
LaunchUtils.LauncherError.GENERIC -> listOf(
stringResource(R.string.load_failed_recommendation_reinstall),
stringResource(R.string.load_failed_recommendation_update),
stringResource(R.string.load_failed_recommendation_report)
)
}
Column(modifier = modifier) {
Text(headline)
recommendations.forEach { r ->
Text("\u2022\u00A0\u00A0$r")
}
}
}
@Composable
fun ErrorInfoTitle(failureReason: LaunchUtils.LauncherError) {
val message = when (failureReason) {
LaunchUtils.LauncherError.LINKER_NEEDS_64BIT,
LaunchUtils.LauncherError.LINKER_NEEDS_32BIT,
LaunchUtils.LauncherError.LINKER_FAILED -> stringResource(R.string.load_failed_link_error)
LaunchUtils.LauncherError.GENERIC -> stringResource(R.string.load_failed_generic_error)
}
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painterResource(R.drawable.icon_error),
contentDescription = stringResource(R.string.launcher_error_icon_alt),
modifier = Modifier.size(28.dp)
)
Text(message, style = Typography.headlineMedium)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ErrorInfoSheet(failureInfo: LoadFailureInfo, onDismiss: () -> Unit) {
val sheetState = rememberModalBottomSheetState()
ModalBottomSheet(onDismissRequest = { onDismiss() }, sheetState = sheetState) {
Column(modifier = Modifier.padding(horizontal = 24.dp)) {
ErrorInfoTitle(failureInfo.title)
Spacer(Modifier.size(16.dp))
ErrorInfoBody(failureInfo.title)
Column(modifier = Modifier.padding(vertical = 8.dp)) {
Text(
stringResource(R.string.launcher_error_details),
style = Typography.headlineSmall
)
Spacer(Modifier.size(8.dp))
Text(
failureInfo.description,
fontFamily = FontFamily.Monospace,
style = Typography.bodyMedium
)
}
val clipboardManager = LocalClipboardManager.current
val context = LocalContext.current
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp),
horizontalArrangement = Arrangement.spacedBy(
12.dp, alignment = Alignment.CenterHorizontally
)
) {
Button(onClick = {
val launchIntent = Intent(context, ApplicationLogsActivity::class.java)
context.startActivity(launchIntent)
}) {
Icon(
painterResource(R.drawable.icon_description),
contentDescription = null
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(stringResource(R.string.launcher_error_view_logs))
}
FilledTonalButton(onClick = {
val message = failureInfo.extendedMessage ?: failureInfo.description
clipboardManager.setText(AnnotatedString(message))
}) {
Icon(
painterResource(R.drawable.icon_content_copy),
contentDescription = null
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(stringResource(R.string.launcher_error_copy))
}
}
Spacer(Modifier.size(8.dp))
}
}
}
fun onLaunch(context: Context, safeMode: Boolean = false) {
if (safeMode) {
GeodeUtils.setAdditionalLaunchArguments(GeodeUtils.ARGUMENT_SAFE_MODE)
@@ -444,7 +561,7 @@ fun onSettings(context: Context) {
fun MainScreen(
gdInstalled: Boolean = true,
geodePreinstalled: Boolean = true,
disableAutomaticLaunch: Boolean = false,
loadFailureInfo: LoadFailureInfo? = null,
releaseViewModel: ReleaseViewModel = viewModel(factory = ReleaseViewModel.Factory)
) {
val context = LocalContext.current
@@ -473,144 +590,175 @@ fun MainScreen(
}
}
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.geode_logo),
contentDescription = context.getString(R.string.launcher_logo_alt),
modifier = Modifier.size(136.dp, 136.dp)
)
Text(
context.getString(R.string.launcher_title),
fontSize = 32.sp,
modifier = Modifier.padding(12.dp)
)
val hasError = loadFailureInfo != null
var showErrorInfo by remember { mutableStateOf(false) }
if (gdInstalled && geodeInstalled) {
val stopLaunch = releaseViewModel.isInUpdate || disableAutomaticLaunch || showSafeModeDialog
if (shouldAutomaticallyLaunch && !stopLaunch) {
val countdownTimer = useCountdownTimer(
time = 3000,
onCountdownFinish = { beginLaunch = true }
)
val snackbarHostState = remember { SnackbarHostState() }
if (countdownTimer != 0L) {
Text(
pluralStringResource(
R.plurals.automatically_load_countdown,
countdownTimer.toInt(),
countdownTimer
),
style = Typography.bodyMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer,
)
Spacer(Modifier.size(12.dp))
LaunchedEffect(hasError) {
if (hasError) {
val res = snackbarHostState.showSnackbar(
message = context.getString(R.string.launcher_failed_to_load),
actionLabel = context.getString(R.string.launcher_error_more),
duration = SnackbarDuration.Indefinite,
)
when (res) {
SnackbarResult.ActionPerformed -> {
showErrorInfo = true
}
SnackbarResult.Dismissed -> {}
}
}
}
// compose apis don't provide a good way of adding long press to a button
val interactionSource = remember { MutableInteractionSource() }
val viewConfiguration = LocalViewConfiguration.current
val haptics = LocalHapticFeedback.current
if (showErrorInfo && loadFailureInfo != null) {
ErrorInfoSheet(loadFailureInfo, onDismiss = { showErrorInfo = false })
}
LaunchedEffect(interactionSource) {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> {
launchInSafeMode = false
Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) }
) { innerPadding ->
Column(
modifier = Modifier
.padding(innerPadding)
.fillMaxSize()
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painter = painterResource(id = R.drawable.geode_logo),
contentDescription = context.getString(R.string.launcher_logo_alt),
modifier = Modifier.size(136.dp, 136.dp)
)
Text(
context.getString(R.string.launcher_title),
fontSize = 32.sp,
modifier = Modifier.padding(12.dp)
)
delay(viewConfiguration.longPressTimeoutMillis)
if (gdInstalled && geodeInstalled) {
val stopLaunch = releaseViewModel.isInUpdate || hasError || showSafeModeDialog
if (shouldAutomaticallyLaunch && !stopLaunch) {
val countdownTimer = useCountdownTimer(
time = 3000,
onCountdownFinish = { beginLaunch = true }
)
// perform a second delay to make the action more obvious
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
delay(viewConfiguration.longPressTimeoutMillis)
if (countdownTimer != 0L) {
Text(
pluralStringResource(
R.plurals.automatically_load_countdown,
countdownTimer.toInt(),
countdownTimer
),
style = Typography.bodyMedium,
color = MaterialTheme.colorScheme.onSecondaryContainer,
)
Spacer(Modifier.size(12.dp))
}
}
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
// compose apis don't provide a good way of adding long press to a button
val interactionSource = remember { MutableInteractionSource() }
val viewConfiguration = LocalViewConfiguration.current
val haptics = LocalHapticFeedback.current
showSafeModeDialog = true
}
LaunchedEffect(interactionSource) {
interactionSource.interactions.collectLatest { interaction ->
when (interaction) {
is PressInteraction.Press -> {
launchInSafeMode = false
is PressInteraction.Release -> {
if (!showSafeModeDialog) {
beginLaunch = true
delay(viewConfiguration.longPressTimeoutMillis)
// perform a second delay to make the action more obvious
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
delay(viewConfiguration.longPressTimeoutMillis)
haptics.performHapticFeedback(HapticFeedbackType.LongPress)
showSafeModeDialog = true
}
is PressInteraction.Release -> {
if (!showSafeModeDialog) {
beginLaunch = true
}
}
}
}
}
}
Row {
Button(
onClick = { },
enabled = !releaseViewModel.isInUpdate,
interactionSource = interactionSource
) {
Icon(
Icons.Filled.PlayArrow,
contentDescription = context.getString(R.string.launcher_launch_icon_alt)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_launch))
Row {
Button(
onClick = { },
enabled = !releaseViewModel.isInUpdate,
interactionSource = interactionSource
) {
Icon(
Icons.Filled.PlayArrow,
contentDescription = context.getString(R.string.launcher_launch_icon_alt)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_launch))
}
Spacer(Modifier.size(2.dp))
IconButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
)
}
}
Spacer(Modifier.size(2.dp))
IconButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
)
}
}
} else if (gdInstalled) {
Text(
context.getString(R.string.geode_download_title),
modifier = Modifier.padding(12.dp)
)
Row {
Button(
onClick = { releaseViewModel.runReleaseCheck(true) },
enabled = !releaseViewModel.isInUpdate
) {
Icon(
painterResource(R.drawable.icon_download),
contentDescription = context.getString(R.string.launcher_download_icon_alt)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_download))
}
Spacer(Modifier.size(2.dp))
IconButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
)
}
}
} else {
Text(
context.getString(R.string.game_not_found),
modifier = Modifier.padding(12.dp)
)
OutlinedButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
} else if (gdInstalled) {
Text(
context.getString(R.string.geode_download_title),
modifier = Modifier.padding(12.dp)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_settings))
}
}
UpdateCard(
releaseViewModel,
modifier = Modifier
.padding(12.dp)
)
Row {
Button(
onClick = { releaseViewModel.runReleaseCheck(true) },
enabled = !releaseViewModel.isInUpdate
) {
Icon(
painterResource(R.drawable.icon_download),
contentDescription = context.getString(R.string.launcher_download_icon_alt)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_download))
}
Spacer(Modifier.size(2.dp))
IconButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
)
}
}
} else {
Text(
context.getString(R.string.game_not_found),
modifier = Modifier.padding(12.dp)
)
OutlinedButton(onClick = { onSettings(context) }) {
Icon(
Icons.Filled.Settings,
contentDescription = context.getString(R.string.launcher_settings_icon_alt)
)
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
Text(context.getString(R.string.launcher_settings))
}
}
UpdateCard(
releaseViewModel,
modifier = Modifier
.padding(12.dp)
)
}
}
if (beginLaunch) {
@@ -15,8 +15,4 @@ object Constants {
const val SUPPORTED_VERSION_CODE = 38L
const val SUPPORTED_VERSION_STRING = "2.205"
const val LAUNCHER_KEY_RETURN_TITLE = "return_title"
const val LAUNCHER_KEY_RETURN_MESSAGE = "return_message"
}
@@ -155,4 +155,15 @@ object LaunchUtils {
aspMethod.invoke(assetManager, it)
}
}
enum class LauncherError {
LINKER_NEEDS_64BIT,
LINKER_NEEDS_32BIT,
LINKER_FAILED,
GENERIC
}
const val LAUNCHER_KEY_RETURN_ERROR = "return_error"
const val LAUNCHER_KEY_RETURN_MESSAGE = "return_message"
const val LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE = "return_extended_message"
}
@@ -0,0 +1,11 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal"
android:autoMirrored="true">
<path
android:fillColor="@android:color/white"
android:pathData="M360,720Q327,720 303.5,696.5Q280,673 280,640L280,160Q280,127 303.5,103.5Q327,80 360,80L720,80Q753,80 776.5,103.5Q800,127 800,160L800,640Q800,673 776.5,696.5Q753,720 720,720L360,720ZM360,640L720,640Q720,640 720,640Q720,640 720,640L720,160Q720,160 720,160Q720,160 720,160L360,160Q360,160 360,160Q360,160 360,160L360,640Q360,640 360,640Q360,640 360,640ZM200,880Q167,880 143.5,856.5Q120,833 120,800L120,240L200,240L200,800Q200,800 200,800Q200,800 200,800L640,800L640,880L200,880ZM360,640Q360,640 360,640Q360,640 360,640L360,160Q360,160 360,160Q360,160 360,160L360,160Q360,160 360,160Q360,160 360,160L360,640Q360,640 360,640Q360,640 360,640L360,640Z"/>
</vector>
@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M320,720L640,720L640,640L320,640L320,720ZM320,560L640,560L640,480L320,480L320,560ZM240,880Q207,880 183.5,856.5Q160,833 160,800L160,160Q160,127 183.5,103.5Q207,80 240,80L560,80L800,320L800,800Q800,833 776.5,856.5Q753,880 720,880L240,880ZM520,360L520,160L240,160Q240,160 240,160Q240,160 240,160L240,800Q240,800 240,800Q240,800 240,800L720,800Q720,800 720,800Q720,800 720,800L720,360L520,360ZM240,160L240,160L240,360L240,360L240,160L240,360L240,360L240,800Q240,800 240,800Q240,800 240,800L240,800Q240,800 240,800Q240,800 240,800L240,160Q240,160 240,160Q240,160 240,160Z"/>
</vector>
+16 -5
View File
@@ -32,6 +32,12 @@
<string name="launcher_update_dismiss">Dismiss</string>
<string name="safe_mode_enable_title">Enable safe mode?</string>
<string name="safe_mode_enable_description">Safe mode disables loading of all mods, allowing one to remove problematic mods or diagnose crashes. If you\'re not experiencing issues with Geode, you should probably not be using safe mode.</string>
<string name="launcher_failed_to_load">Geode failed to load.</string>
<string name="launcher_error_more">Learn more</string>
<string name="launcher_error_details">Error details</string>
<string name="launcher_error_view_logs">View logs</string>
<string name="launcher_error_copy_error_alt">Copy</string>
<string name="launcher_error_copy">Copy details</string>
<!-- Updater strings -->
<string name="release_fetch_failed">Update failed.\n%1$s</string>
@@ -48,11 +54,16 @@
<!-- GeometryDashActivity strings -->
<string name="load_failed_link_error">Failed to link libraries</string>
<string name="load_failed_link_error_description">Game libraries could not be found. This may occur if the architecture or version of the launcher and Geometry Dash do not match, or from a system bug.\nCheck application logs for more details.</string>
<string name="load_failed_abi_error_need_64bit_description">The architecture of the launcher and Geometry Dash do not match. Please reinstall Geometry Dash from the Play Store or use the 64-bit version of the launcher to resolve this issue.\nCheck application logs for more details.</string>
<string name="load_failed_abi_error_need_32bit_description">The architecture of the launcher and Geometry Dash do not match. Please reinstall Geometry Dash from the Play Store or use the 32-bit version of the launcher to resolve this issue.\nCheck application logs for more details.</string>
<string name="load_failed_generic_error">Load failed</string>
<string name="load_failed_generic_error_description">Uncaught error during game load: `%1$s`.\nCheck application logs for more details.</string>
<string name="load_failed_link_error_description">Game libraries could not be found. Try to:</string>
<string name="load_failed_abi_error">The architecture of the launcher and Geometry Dash do not match. Try to:</string>
<string name="load_failed_generic_error">Failed to load Geode</string>
<string name="load_failed_generic_error_description">Uncaught error during game load. Try to:</string>
<string name="load_failed_recommendation_reinstall">Reinstall Geometry Dash from the Play Store.</string>
<string name="load_failed_recommendation_switch_32bit_abi">Install the 32-bit version of the launcher.</string>
<string name="load_failed_recommendation_switch_64bit_abi">Install the non 32-bit version of the launcher.</string>
<string name="load_failed_recommendation_update">Verify that the latest versions of the launcher and Geometry Dash are installed.</string>
<string name="load_failed_recommendation_report">Report this issue to the developers.</string>
<string name="message_box_cancel">Cancel</string>
<string name="message_box_accept">OK</string>
<string name="message_box_continue">Continue</string>