From 375f771e8abfad73a3317cc9d3a6fd297fd1faf8 Mon Sep 17 00:00:00 2001
From: qimiko <25387744+qimiko@users.noreply.github.com>
Date: Thu, 20 Jun 2024 23:07:09 -0700
Subject: [PATCH] add developer options menu
---
app/src/main/AndroidManifest.xml | 6 +-
.../preferences/DeveloperSettingsActivity.kt | 169 ++++++++++++++++++
.../launcher/preferences/SettingsActivity.kt | 62 +++----
.../preferences/SettingsComponents.kt | 45 +++--
.../geode/launcher/updater/ReleaseManager.kt | 4 +
.../com/geode/launcher/utils/Components.kt | 46 +++++
.../geode/launcher/utils/PreferenceUtils.kt | 5 +-
.../main/res/drawable/icon_data_object.xml | 10 ++
app/src/main/res/values/strings.xml | 12 +-
9 files changed, 304 insertions(+), 55 deletions(-)
create mode 100644 app/src/main/java/com/geode/launcher/preferences/DeveloperSettingsActivity.kt
create mode 100644 app/src/main/java/com/geode/launcher/utils/Components.kt
create mode 100644 app/src/main/res/drawable/icon_data_object.xml
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 9a2d775..122636e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -41,7 +41,11 @@
+
stringResource(R.string.preference_release_channel_beta)
+ 2 -> stringResource(R.string.preference_release_channel_nightly)
+ else -> stringResource(R.string.preference_release_channel_stable)
+}
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun DeveloperSettingsScreen(onBackPressedDispatcher: OnBackPressedDispatcher?) {
+ val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
+
+ Scaffold(
+ modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
+ topBar = {
+ Column {
+ TopAppBar(
+ navigationIcon = {
+ IconButton(onClick = { onBackPressedDispatcher?.onBackPressed() }) {
+ Icon(
+ imageVector = Icons.AutoMirrored.Filled.ArrowBack,
+ contentDescription = stringResource(R.string.back_icon_alt)
+ )
+ }
+ },
+ title = {
+ Text(
+ stringResource(R.string.title_activity_developer_settings),
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ },
+ scrollBehavior = scrollBehavior,
+ )
+ }
+ }
+ ) { innerPadding ->
+ Column(
+ Modifier
+ .padding(innerPadding)
+ .fillMaxWidth()
+ .verticalScroll(rememberScrollState()),
+ verticalArrangement = Arrangement.spacedBy(8.dp)
+ ) {
+
+ val developerMode by PreferenceUtils.useBooleanPreference(PreferenceUtils.Key.DEVELOPER_MODE)
+
+ SettingsCard(
+ title = stringResource(R.string.preference_developer_mode),
+ preferenceKey = PreferenceUtils.Key.DEVELOPER_MODE,
+ )
+
+ InlineText(
+ stringResource(R.string.preference_developer_mode_about)
+ )
+
+ HorizontalDivider()
+
+ if (developerMode) {
+ OptionsGroup(title = stringResource(R.string.preference_category_gameplay)) {
+ SettingsStringCard(
+ title = stringResource(R.string.preference_launch_arguments_name),
+ dialogTitle = stringResource(R.string.preference_launch_arguments_set_title),
+ preferenceKey = PreferenceUtils.Key.LAUNCH_ARGUMENTS,
+ filterInput = { it.filter { c ->
+ // if only there was a better way to define this!
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}<>[]?:;'\"~`-_+=\\| ".contains(c)
+ }}
+ )
+ }
+
+ OptionsGroup(title = stringResource(R.string.preference_category_updater)) {
+ SettingsSelectCard(
+ title = stringResource(R.string.preference_release_channel_tag_name),
+ dialogTitle = stringResource(R.string.preference_release_channel_select),
+ maxVal = 2,
+ preferenceKey = PreferenceUtils.Key.RELEASE_CHANNEL_TAG,
+ toLabel = { releaseChannelToKey(it) }
+ )
+
+ ElevatedCard(modifier = Modifier.padding(horizontal = 16.dp, vertical = 4.dp)) {
+ LabelledText(
+ label = stringResource(R.string.preference_developer_update_notice),
+ icon = {
+ Icon(Icons.Filled.Info, null)
+ },
+ modifier = Modifier.padding(8.dp)
+ )
+ }
+ }
+
+ OptionsGroup(title = stringResource(R.string.preference_category_testing)) {
+ SettingsCard(
+ title = stringResource(R.string.preference_enable_redesign),
+ preferenceKey = PreferenceUtils.Key.ENABLE_REDESIGN,
+ )
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/geode/launcher/preferences/SettingsActivity.kt b/app/src/main/java/com/geode/launcher/preferences/SettingsActivity.kt
index 60a48da..d269acb 100644
--- a/app/src/main/java/com/geode/launcher/preferences/SettingsActivity.kt
+++ b/app/src/main/java/com/geode/launcher/preferences/SettingsActivity.kt
@@ -36,6 +36,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.text.style.TextOverflow
@@ -205,13 +206,6 @@ fun displayOptionToKey(option: Int): String {
}
}
-@Composable
-fun releaseChannelToKey(option: Int): String = when (option) {
- 1 -> stringResource(R.string.preference_release_channel_beta)
- 2 -> stringResource(R.string.preference_release_channel_nightly)
- else -> stringResource(R.string.preference_release_channel_stable)
-}
-
fun updateTheme(context: Context, theme: Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val uiModeManager = context.getSystemService(Context.UI_MODE_SERVICE) as UiModeManager
@@ -238,6 +232,11 @@ fun onOpenLogs(context: Context) {
context.startActivity(launchIntent)
}
+fun onOpenDeveloperOptions(context: Context) {
+ val launchIntent = Intent(context, DeveloperSettingsActivity::class.java)
+ context.startActivity(launchIntent)
+}
+
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SettingsScreen(
@@ -316,6 +315,13 @@ fun SettingsScreen(
title = stringResource(R.string.preference_black_background_name),
preferenceKey = PreferenceUtils.Key.BLACK_BACKGROUND
)
+ OptionsButton(
+ title = stringResource(R.string.preferences_open_file_manager),
+ onClick = { onOpenFileManager(context) }
+ )
+ }
+
+ OptionsGroup(stringResource(R.string.preference_category_gameplay)) {
SettingsCard(
title = context.getString(R.string.preference_load_automatically_name),
description = context.getString(R.string.preference_load_automatically_description),
@@ -331,14 +337,10 @@ fun SettingsScreen(
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
SettingsCard(
- title = context.getString(R.string.preference_force_hrr),
+ title = stringResource(R.string.preference_force_hrr),
preferenceKey = PreferenceUtils.Key.FORCE_HRR,
)
}
- OptionsButton(
- title = stringResource(R.string.preferences_open_file_manager),
- onClick = { onOpenFileManager(context) }
- )
}
OptionsGroup(context.getString(R.string.preference_category_updater)) {
@@ -346,13 +348,6 @@ fun SettingsScreen(
title = context.getString(R.string.preference_update_automatically_name),
preferenceKey = PreferenceUtils.Key.UPDATE_AUTOMATICALLY,
)
- SettingsSelectCard(
- title = stringResource(R.string.preference_release_channel_tag_name),
- dialogTitle = stringResource(R.string.preference_release_channel_select),
- maxVal = 2,
- preferenceKey = PreferenceUtils.Key.RELEASE_CHANNEL_TAG,
- toLabel = { releaseChannelToKey(it) }
- )
OptionsCard(
title = {
OptionsTitle(
@@ -380,28 +375,25 @@ fun SettingsScreen(
}
OptionsGroup(stringResource(R.string.preference_category_developer)) {
- SettingsStringCard(
- title = stringResource(R.string.preference_launch_arguments_name),
- dialogTitle = stringResource(R.string.preference_launch_arguments_set_title),
- preferenceKey = PreferenceUtils.Key.LAUNCH_ARGUMENTS,
- filterInput = { it.filter { c ->
- // if only there was a better way to define this!
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890!@#$%^&*(){}<>[]?:;'\"~`-_+=\\| ".contains(c)
- }}
+ OptionsButton(
+ title = stringResource(R.string.preferences_view_logs),
+ icon = {
+ Icon(painterResource(R.drawable.icon_description), contentDescription = null)
+ },
+ onClick = { onOpenLogs(context) }
+ )
+ OptionsButton(
+ title = stringResource(R.string.preference_open_developer_options),
+ icon = {
+ Icon(painterResource(R.drawable.icon_data_object), contentDescription = null)
+ },
+ onClick = { onOpenDeveloperOptions(context) }
)
OptionsButton(
title = context.getString(R.string.preferences_copy_external_button),
description = LaunchUtils.getBaseDirectory(context).path,
onClick = { onOpenFolder(context) }
)
- OptionsButton(
- title = stringResource(R.string.preferences_view_logs),
- onClick = { onOpenLogs(context) }
- )
- SettingsCard(
- title = context.getString(R.string.preference_enable_redesign),
- preferenceKey = PreferenceUtils.Key.ENABLE_REDESIGN,
- )
}
Text(
diff --git a/app/src/main/java/com/geode/launcher/preferences/SettingsComponents.kt b/app/src/main/java/com/geode/launcher/preferences/SettingsComponents.kt
index 8a0ebb6..9c9beba 100644
--- a/app/src/main/java/com/geode/launcher/preferences/SettingsComponents.kt
+++ b/app/src/main/java/com/geode/launcher/preferences/SettingsComponents.kt
@@ -40,6 +40,7 @@ import androidx.compose.ui.window.Dialog
import com.geode.launcher.R
import com.geode.launcher.ui.theme.GeodeLauncherTheme
import com.geode.launcher.ui.theme.Typography
+import com.geode.launcher.utils.LabelledText
import com.geode.launcher.utils.PreferenceUtils
@@ -278,12 +279,13 @@ fun SelectDialog(
}
@Composable
-fun OptionsButton(title: String, description: String? = null, onClick: () -> Unit) {
+fun OptionsButton(title: String, description: String? = null, icon: (@Composable () -> Unit)? = null, onClick: () -> Unit) {
OptionsCard(
title = {
OptionsTitle(
title = title,
- description = description
+ description = description,
+ icon = icon
)
},
modifier = Modifier
@@ -292,7 +294,7 @@ fun OptionsButton(title: String, description: String? = null, onClick: () -> Uni
}
@Composable
-fun SettingsCard(title: String, description: String? = null, preferenceKey: PreferenceUtils.Key) {
+fun SettingsCard(title: String, description: String? = null, icon: (@Composable () -> Unit)? = null, preferenceKey: PreferenceUtils.Key) {
val context = LocalContext.current
val settingEnabled = remember {
mutableStateOf(getSetting(context, preferenceKey))
@@ -303,7 +305,8 @@ fun SettingsCard(title: String, description: String? = null, preferenceKey: Pref
OptionsTitle(
Modifier.fillMaxWidth(0.75f),
title = title,
- description = description
+ description = description,
+ icon = icon
)
},
modifier = Modifier.toggleable(
@@ -317,18 +320,23 @@ fun SettingsCard(title: String, description: String? = null, preferenceKey: Pref
}
@Composable
-fun OptionsTitle(modifier: Modifier = Modifier, title: String, description: String? = null) {
- Column(
- modifier,
- verticalArrangement = Arrangement.spacedBy(4.dp),
- ) {
- Text(title)
- if (!description.isNullOrEmpty()) {
- Text(
- description,
- style = Typography.labelMedium,
- color = MaterialTheme.colorScheme.onSecondaryContainer
- )
+fun OptionsTitle(modifier: Modifier = Modifier, title: String, description: String? = null, icon: (@Composable () -> Unit)? = null) {
+ Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
+ if (icon != null) {
+ icon()
+ }
+ Column(
+ modifier,
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ Text(title)
+ if (!description.isNullOrEmpty()) {
+ Text(
+ description,
+ style = Typography.labelMedium,
+ color = MaterialTheme.colorScheme.onSecondaryContainer
+ )
+ }
}
}
}
@@ -348,6 +356,11 @@ fun OptionsCard(modifier: Modifier = Modifier, title: @Composable () -> Unit, co
}
}
+@Composable
+fun InlineText(label: String, icon: @Composable (() -> Unit)? = null, modifier: Modifier = Modifier) {
+ LabelledText(label = label, icon = icon, modifier = modifier.padding(horizontal = 16.dp, vertical = 4.dp))
+}
+
@Preview(showBackground = true)
@Composable
fun OptionsCardPreview() {
diff --git a/app/src/main/java/com/geode/launcher/updater/ReleaseManager.kt b/app/src/main/java/com/geode/launcher/updater/ReleaseManager.kt
index 95dc779..d2f31ae 100644
--- a/app/src/main/java/com/geode/launcher/updater/ReleaseManager.kt
+++ b/app/src/main/java/com/geode/launcher/updater/ReleaseManager.kt
@@ -182,6 +182,10 @@ class ReleaseManager private constructor(
}
val sharedPreferences = PreferenceUtils.get(applicationContext)
+ if (!sharedPreferences.getBoolean(PreferenceUtils.Key.DEVELOPER_MODE)) {
+ return false
+ }
+
val originalFileHash = sharedPreferences.getString(PreferenceUtils.Key.CURRENT_RELEASE_MODIFIED)
?: return false
diff --git a/app/src/main/java/com/geode/launcher/utils/Components.kt b/app/src/main/java/com/geode/launcher/utils/Components.kt
new file mode 100644
index 0000000..ce3fb5b
--- /dev/null
+++ b/app/src/main/java/com/geode/launcher/utils/Components.kt
@@ -0,0 +1,46 @@
+package com.geode.launcher.utils
+
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.text.InlineTextContent
+import androidx.compose.foundation.text.appendInlineContent
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.text.Placeholder
+import androidx.compose.ui.text.PlaceholderVerticalAlign
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.unit.sp
+
+@Composable
+fun LabelledText(label: String, icon: @Composable (() -> Unit)? = null, modifier: Modifier = Modifier) {
+ if (icon != null) {
+ val iconId = "inlineIcon"
+ val paddingId = "paddingIcon"
+
+ val labelText = buildAnnotatedString {
+ appendInlineContent(iconId, "[x]")
+ appendInlineContent(paddingId, " ")
+ append(label)
+ }
+
+ val inlineContent = mapOf(
+ Pair(iconId, InlineTextContent(
+ Placeholder(
+ width = 24.sp,
+ height = 24.sp,
+ placeholderVerticalAlign = PlaceholderVerticalAlign.TextCenter
+ )
+ ) {
+ icon()
+ }),
+ Pair(paddingId, InlineTextContent(Placeholder(width = 6.sp, height = 24.sp, placeholderVerticalAlign = PlaceholderVerticalAlign.Center)) {
+ Spacer(modifier = Modifier.fillMaxWidth())
+ })
+ )
+
+ Text(text = labelText, inlineContent = inlineContent, modifier = modifier)
+ } else {
+ Text(label, modifier = modifier)
+ }
+}
diff --git a/app/src/main/java/com/geode/launcher/utils/PreferenceUtils.kt b/app/src/main/java/com/geode/launcher/utils/PreferenceUtils.kt
index f98215c..e33e6c3 100644
--- a/app/src/main/java/com/geode/launcher/utils/PreferenceUtils.kt
+++ b/app/src/main/java/com/geode/launcher/utils/PreferenceUtils.kt
@@ -138,12 +138,14 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
DISPLAY_MODE,
FORCE_HRR,
ENABLE_REDESIGN,
- RELEASE_CHANNEL_TAG
+ RELEASE_CHANNEL_TAG,
+ DEVELOPER_MODE
}
private fun defaultValueForBooleanKey(key: Key): Boolean {
return when (key) {
Key.UPDATE_AUTOMATICALLY, Key.FORCE_HRR -> true
+ Key.DEVELOPER_MODE -> BuildConfig.DEBUG
else -> false
}
}
@@ -172,6 +174,7 @@ class PreferenceUtils(private val sharedPreferences: SharedPreferences) {
Key.FORCE_HRR -> "PreferenceForceHighRefreshRate"
Key.ENABLE_REDESIGN -> "PreferenceEnableRedesign"
Key.RELEASE_CHANNEL_TAG -> "PreferenceReleaseChannelTag"
+ Key.DEVELOPER_MODE -> "PreferenceDeveloperMode"
}
}
diff --git a/app/src/main/res/drawable/icon_data_object.xml b/app/src/main/res/drawable/icon_data_object.xml
new file mode 100644
index 0000000..022bdf3
--- /dev/null
+++ b/app/src/main/res/drawable/icon_data_object.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 7508cc6..63ea097 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -135,6 +135,8 @@
Enable automatic updates
Use nightly release channel
+ Gameplay
+
Release channel
Select release channel
Stable
@@ -151,11 +153,13 @@
No internet found.
An update is already in progress!
- Developer
+ Advanced
Launch arguments
Set launch arguments
Clear
+ Open developer options
+
Application Logs
No logs available.
@@ -170,5 +174,9 @@
launcher_logcat.txt
Show Crash Buffer
Enabled
- AltMainActivity
+
+ Developer Settings
+ Enable developer options
+ Developer mode enables some options that may make the game unstable. Please do not modify them unless you know what you\'re doing!\nThese options may change behavior, or be removed entirely in future updates.
+ Enabling developer mode automatically disallows the updater from overwriting custom loader builds.
\ No newline at end of file