forked from geode-sdk/android-launcher
redo load failed popup
This commit is contained in:
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>
|
||||
@@ -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>
|
||||
|
||||
Reference in new issue
Block a user