mirror of
https://github.com/geode-sdk/android-launcher.git
synced 2026-10-22 00:42:38 +00:00
tell people where to go for an updated game
This commit is contained in:
6 files changed
+144
-18
No files matched your search
@@ -3,6 +3,7 @@ package com.geode.launcher
|
|||||||
import android.content.ActivityNotFoundException
|
import android.content.ActivityNotFoundException
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
@@ -12,10 +13,13 @@ import androidx.activity.ComponentActivity
|
|||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
import androidx.activity.enableEdgeToEdge
|
import androidx.activity.enableEdgeToEdge
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
import androidx.compose.foundation.interaction.PressInteraction
|
import androidx.compose.foundation.interaction.PressInteraction
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.AbsoluteRoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.ClickableText
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.PlayArrow
|
import androidx.compose.material.icons.filled.PlayArrow
|
||||||
@@ -34,6 +38,7 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
import androidx.compose.ui.platform.LocalClipboardManager
|
import androidx.compose.ui.platform.LocalClipboardManager
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@@ -44,8 +49,10 @@ import androidx.compose.ui.res.painterResource
|
|||||||
import androidx.compose.ui.res.pluralStringResource
|
import androidx.compose.ui.res.pluralStringResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
|
import androidx.compose.ui.text.TextStyle
|
||||||
import androidx.compose.ui.text.font.FontFamily
|
import androidx.compose.ui.text.font.FontFamily
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.text.style.TextDecoration
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
@@ -594,6 +601,82 @@ fun ErrorInfoActions(extraDetails: String?, modifier: Modifier = Modifier) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun onDownloadGame(context: Context) {
|
||||||
|
val appUrl = "https://play.google.com/store/apps/details?id=${Constants.PACKAGE_NAME}"
|
||||||
|
try {
|
||||||
|
val intent = Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
data = Uri.parse(appUrl)
|
||||||
|
setPackage("com.android.vending")
|
||||||
|
}
|
||||||
|
context.startActivity(intent)
|
||||||
|
} catch (_: ActivityNotFoundException) {
|
||||||
|
Toast.makeText(context, R.string.no_activity_found, Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun GooglePlayBadge(modifier: Modifier = Modifier) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.google_play_badge),
|
||||||
|
contentDescription = stringResource(R.string.launcher_download_game),
|
||||||
|
modifier = modifier
|
||||||
|
.width(196.dp)
|
||||||
|
.clip(AbsoluteRoundedCornerShape(4.dp))
|
||||||
|
.clickable { onDownloadGame(context) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun DownloadRecommendation(needsUniversal: Boolean, modifier: Modifier = Modifier) {
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
val context = LocalContext.current
|
||||||
|
val version = BuildConfig.VERSION_NAME
|
||||||
|
|
||||||
|
val showDownload = remember {
|
||||||
|
!GamePackageUtils.isGameInstalled(context.packageManager) ||
|
||||||
|
!GamePackageUtils.identifyGameLegitimacy(context.packageManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
val downloadBase = "https://github.com/geode-sdk/android-launcher/releases/download/$version"
|
||||||
|
val legacyDownloadUrl = "$downloadBase/geode-launcher-v$version-android32.apk"
|
||||||
|
val universalDownloadUrl = "$downloadBase/geode-launcher-v$version.apk"
|
||||||
|
|
||||||
|
val downloadUrl = if (needsUniversal)
|
||||||
|
universalDownloadUrl else legacyDownloadUrl
|
||||||
|
|
||||||
|
val downloadText = if (needsUniversal)
|
||||||
|
stringResource(R.string.launcher_download_universal)
|
||||||
|
else
|
||||||
|
stringResource(R.string.launcher_download_32bit)
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = modifier,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(
|
||||||
|
12.dp, alignment = Alignment.CenterVertically
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
ClickableText(
|
||||||
|
text = AnnotatedString(downloadText),
|
||||||
|
onClick = {
|
||||||
|
uriHandler.openUri(downloadUrl)
|
||||||
|
},
|
||||||
|
|
||||||
|
style = TextStyle.Default.copy(
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
textDecoration = TextDecoration.Underline
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
if (showDownload) {
|
||||||
|
GooglePlayBadge(
|
||||||
|
modifier = Modifier.align(Alignment.CenterHorizontally)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ErrorInfoSheet(failureInfo: LoadFailureInfo, onDismiss: () -> Unit) {
|
fun ErrorInfoSheet(failureInfo: LoadFailureInfo, onDismiss: () -> Unit) {
|
||||||
@@ -611,19 +694,28 @@ fun ErrorInfoSheet(failureInfo: LoadFailureInfo, onDismiss: () -> Unit) {
|
|||||||
|
|
||||||
ErrorInfoBody(failureInfo.title)
|
ErrorInfoBody(failureInfo.title)
|
||||||
|
|
||||||
if (failureInfo.description != null) {
|
if (failureInfo.description != null && !failureInfo.title.isAbiFailure()) {
|
||||||
ErrorInfoDescription(
|
ErrorInfoDescription(
|
||||||
failureInfo.description,
|
failureInfo.description,
|
||||||
modifier = Modifier.padding(vertical = 8.dp)
|
modifier = Modifier.padding(vertical = 8.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
ErrorInfoActions(
|
if (failureInfo.title.isAbiFailure()) {
|
||||||
extraDetails = failureInfo.extendedMessage ?: failureInfo.description,
|
DownloadRecommendation(
|
||||||
modifier = Modifier
|
needsUniversal = failureInfo.title == LaunchUtils.LauncherError.LINKER_NEEDS_64BIT,
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(top = 8.dp, bottom = 16.dp)
|
.fillMaxWidth()
|
||||||
)
|
.padding(top = 8.dp, bottom = 16.dp)
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
ErrorInfoActions(
|
||||||
|
extraDetails = failureInfo.extendedMessage ?: failureInfo.description,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(top = 8.dp, bottom = 16.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -782,12 +874,22 @@ fun PlayButton(
|
|||||||
fun LaunchBlockedLabel(text: String) {
|
fun LaunchBlockedLabel(text: String) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
|
val showDownload = remember {
|
||||||
|
!GamePackageUtils.isGameInstalled(context.packageManager) ||
|
||||||
|
!GamePackageUtils.identifyGameLegitimacy(context.packageManager)
|
||||||
|
}
|
||||||
|
|
||||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||||
Text(text, modifier = Modifier.padding(12.dp))
|
Text(text, textAlign = TextAlign.Center, modifier = Modifier.padding(12.dp))
|
||||||
OutlinedButton(onClick = { onSettings(context) }) {
|
|
||||||
Icon(Icons.Filled.Settings, contentDescription = null)
|
if (showDownload) {
|
||||||
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
GooglePlayBadge()
|
||||||
Text(context.getString(R.string.launcher_settings))
|
} else {
|
||||||
|
OutlinedButton(onClick = { onSettings(context) }) {
|
||||||
|
Icon(Icons.Filled.Settings, contentDescription = null)
|
||||||
|
Spacer(Modifier.size(ButtonDefaults.IconSpacing))
|
||||||
|
Text(context.getString(R.string.launcher_settings))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -868,7 +970,11 @@ fun MainScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (gdVersion < Constants.SUPPORTED_VERSION_CODE) {
|
if (gdVersion < Constants.SUPPORTED_VERSION_CODE) {
|
||||||
LaunchBlockedLabel(stringResource(R.string.game_outdated))
|
val versionName = remember {
|
||||||
|
GamePackageUtils.getUnifiedVersionName(context.packageManager)
|
||||||
|
}
|
||||||
|
|
||||||
|
LaunchBlockedLabel(stringResource(R.string.game_outdated, versionName))
|
||||||
} else {
|
} else {
|
||||||
val stopLaunch = releaseViewModel.isInUpdate || hasError
|
val stopLaunch = releaseViewModel.isInUpdate || hasError
|
||||||
PlayButton(
|
PlayButton(
|
||||||
|
|||||||
@@ -36,6 +36,19 @@ object GamePackageUtils {
|
|||||||
return game.versionName
|
return game.versionName
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val gameVersionMap = mapOf(
|
||||||
|
37L to "2.200",
|
||||||
|
38L to "2.205"
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the version name that more closely aligns to the Windows/macOS releases
|
||||||
|
*/
|
||||||
|
fun getUnifiedVersionName(packageManager: PackageManager): String {
|
||||||
|
val versionCode = getGameVersionCode(packageManager)
|
||||||
|
return gameVersionMap[versionCode] ?: getGameVersionString(packageManager)
|
||||||
|
}
|
||||||
|
|
||||||
fun detectAbiMismatch(context: Context, packageInfo: PackageInfo, loadException: Error): Boolean {
|
fun detectAbiMismatch(context: Context, packageInfo: PackageInfo, loadException: Error): Boolean {
|
||||||
val abi = LaunchUtils.applicationArchitecture
|
val abi = LaunchUtils.applicationArchitecture
|
||||||
val isExtracted =
|
val isExtracted =
|
||||||
|
|||||||
@@ -359,8 +359,7 @@ object GeodeUtils {
|
|||||||
fun getGameVersion(): String {
|
fun getGameVersion(): String {
|
||||||
// these versions should be aligned to windows releases, not what android says
|
// these versions should be aligned to windows releases, not what android says
|
||||||
activity.get()?.run {
|
activity.get()?.run {
|
||||||
val versionCode = GamePackageUtils.getGameVersionCode(packageManager)
|
return GamePackageUtils.getUnifiedVersionName(packageManager)
|
||||||
return gameVersionMap[versionCode] ?: GamePackageUtils.getGameVersionString(packageManager)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ""
|
return ""
|
||||||
|
|||||||
@@ -97,7 +97,11 @@ object LaunchUtils {
|
|||||||
LINKER_NEEDS_32BIT,
|
LINKER_NEEDS_32BIT,
|
||||||
LINKER_FAILED,
|
LINKER_FAILED,
|
||||||
GENERIC,
|
GENERIC,
|
||||||
CRASHED
|
CRASHED;
|
||||||
|
|
||||||
|
fun isAbiFailure(): Boolean {
|
||||||
|
return this == LINKER_NEEDS_32BIT || this == LINKER_NEEDS_64BIT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val LAUNCHER_KEY_RETURN_ERROR = "return_error"
|
const val LAUNCHER_KEY_RETURN_ERROR = "return_error"
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 20 KiB |
@@ -11,10 +11,14 @@
|
|||||||
<string name="launcher_logo_alt">geode logo</string>
|
<string name="launcher_logo_alt">geode logo</string>
|
||||||
<string name="launcher_launch_icon_alt">play icon</string>
|
<string name="launcher_launch_icon_alt">play icon</string>
|
||||||
<string name="launcher_download_icon_alt">download icon</string>
|
<string name="launcher_download_icon_alt">download icon</string>
|
||||||
|
<string name="launcher_purchase_reminder">If you enjoy the game, please consider purchasing it for a smoother launcher experience.</string>
|
||||||
|
<string name="launcher_download_game">Download Game</string>
|
||||||
|
<string name="launcher_download_32bit">Download the 32-bit version of launcher.</string>
|
||||||
|
<string name="launcher_download_universal">Download the non 32-bit version of launcher.</string>
|
||||||
<string name="launcher_settings_icon_alt">settings icon</string>
|
<string name="launcher_settings_icon_alt">settings icon</string>
|
||||||
<string name="launcher_retry_icon_alt">retry icon</string>
|
<string name="launcher_retry_icon_alt">retry icon</string>
|
||||||
<string name="geode_download_title">Geode is not installed.</string>
|
<string name="geode_download_title">Geode is not installed.</string>
|
||||||
<string name="game_outdated">A Geometry Dash update is required.</string>
|
<string name="game_outdated">A Geometry Dash update is required.\nYou currently have version %1$s.</string>
|
||||||
<string name="geode_download_description">Geode is downloading. Please wait.</string>
|
<string name="geode_download_description">Geode is downloading. Please wait.</string>
|
||||||
<plurals name="automatically_load_countdown">
|
<plurals name="automatically_load_countdown">
|
||||||
<item quantity="one">Automatically launching game in %1$d second.</item>
|
<item quantity="one">Automatically launching game in %1$d second.</item>
|
||||||
@@ -65,7 +69,7 @@
|
|||||||
<string name="load_failed_generic_error_description">Uncaught error during game load. Try to:</string>
|
<string name="load_failed_generic_error_description">Uncaught error during game load. Try to:</string>
|
||||||
<string name="load_failed_crashed">Geode crashed</string>
|
<string name="load_failed_crashed">Geode crashed</string>
|
||||||
<string name="load_failed_crashed_description">A crash was reported during the last session. Try to:</string>
|
<string name="load_failed_crashed_description">A crash was reported during the last session. Try to:</string>
|
||||||
<string name="load_failed_recommendation_reinstall">Reinstall Geometry Dash from the Play Store.</string>
|
<string name="load_failed_recommendation_reinstall">Reinstall Geometry Dash.</string>
|
||||||
<string name="load_failed_recommendation_switch_32bit_abi">Install the 32-bit version of the launcher.</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_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_update">Verify that the latest versions of the launcher and Geometry Dash are installed.</string>
|
||||||
|
|||||||
Reference in new issue
Block a user