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