feat(Compose): hide developer settings (#2551)

This commit is contained in:
Ax333l 2025-05-15 20:56:25 +02:00 committed by oSumAtrIX
parent a1f5dd3c26
commit 5153e5e0cb
No known key found for this signature in database
GPG Key ID: A9B3094ACDB604B4
6 changed files with 142 additions and 44 deletions

View File

@ -3,6 +3,7 @@ package app.revanced.manager.domain.manager
import android.content.Context import android.content.Context
import app.revanced.manager.domain.manager.base.BasePreferencesManager import app.revanced.manager.domain.manager.base.BasePreferencesManager
import app.revanced.manager.ui.theme.Theme import app.revanced.manager.ui.theme.Theme
import app.revanced.manager.util.isDebuggable
class PreferencesManager( class PreferencesManager(
context: Context context: Context
@ -28,4 +29,6 @@ class PreferencesManager(
val suggestedVersionSafeguard = booleanPreference("suggested_version_safeguard", true) val suggestedVersionSafeguard = booleanPreference("suggested_version_safeguard", true)
val acknowledgedDownloaderPlugins = stringSetPreference("acknowledged_downloader_plugins", emptySet()) val acknowledgedDownloaderPlugins = stringSetPreference("acknowledged_downloader_plugins", emptySet())
val showDeveloperSettings = booleanPreference("show_developer_settings", context.isDebuggable)
} }

View File

@ -1,5 +1,6 @@
package app.revanced.manager.ui.screen package app.revanced.manager.ui.screen
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@ -7,55 +8,79 @@ import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.* import androidx.compose.material.icons.outlined.*
import androidx.compose.material3.* import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.settings.SettingsListItem import app.revanced.manager.ui.component.settings.SettingsListItem
import app.revanced.manager.ui.model.navigation.Settings import app.revanced.manager.ui.model.navigation.Settings
import org.koin.compose.koinInject
private val settingsSections = listOf( private data class Section(
Triple( @StringRes val name: Int,
R.string.general, @StringRes val description: Int,
R.string.general_description, val image: ImageVector,
Icons.Outlined.Settings val destination: Settings.Destination,
) to Settings.General,
Triple(
R.string.updates,
R.string.updates_description,
Icons.Outlined.Update
) to Settings.Updates,
Triple(
R.string.downloads,
R.string.downloads_description,
Icons.Outlined.Download
) to Settings.Downloads,
Triple(
R.string.import_export,
R.string.import_export_description,
Icons.Outlined.SwapVert
) to Settings.ImportExport,
Triple(
R.string.advanced,
R.string.advanced_description,
Icons.Outlined.Tune
) to Settings.Advanced,
Triple(
R.string.developer_options,
R.string.developer_options_description,
Icons.Outlined.Code
) to Settings.Developer,
Triple(
R.string.about,
R.string.app_name,
Icons.Outlined.Info
) to Settings.About,
) )
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun SettingsScreen(onBackClick: () -> Unit, navigate: (Settings.Destination) -> Unit) { fun SettingsScreen(onBackClick: () -> Unit, navigate: (Settings.Destination) -> Unit) {
val prefs: PreferencesManager = koinInject()
val showDeveloperSettings by prefs.showDeveloperSettings.getAsState()
val settingsSections = remember(showDeveloperSettings) {
listOfNotNull(
Section(
R.string.general,
R.string.general_description,
Icons.Outlined.Settings,
Settings.General
),
Section(
R.string.updates,
R.string.updates_description,
Icons.Outlined.Update,
Settings.Updates
),
Section(
R.string.downloads,
R.string.downloads_description,
Icons.Outlined.Download,
Settings.Downloads
),
Section(
R.string.import_export,
R.string.import_export_description,
Icons.Outlined.SwapVert,
Settings.ImportExport
),
Section(
R.string.advanced,
R.string.advanced_description,
Icons.Outlined.Tune,
Settings.Advanced
),
Section(
R.string.about,
R.string.app_name,
Icons.Outlined.Info,
Settings.About
),
Section(
R.string.developer_options,
R.string.developer_options_description,
Icons.Outlined.Code,
Settings.Developer
).takeIf { showDeveloperSettings }
)
}
Scaffold( Scaffold(
topBar = { topBar = {
AppTopBar( AppTopBar(
@ -69,12 +94,12 @@ fun SettingsScreen(onBackClick: () -> Unit, navigate: (Settings.Destination) ->
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
) { ) {
settingsSections.forEach { (titleDescIcon, destination) -> settingsSections.forEach { (name, description, icon, destination) ->
SettingsListItem( SettingsListItem(
modifier = Modifier.clickable { navigate(destination) }, modifier = Modifier.clickable { navigate(destination) },
headlineContent = stringResource(titleDescIcon.first), headlineContent = stringResource(name),
supportingContent = stringResource(titleDescIcon.second), supportingContent = stringResource(description),
leadingContent = { Icon(titleDescIcon.third, null) } leadingContent = { Icon(icon, null) }
) )
} }
} }

View File

@ -23,16 +23,26 @@ import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedCard import androidx.compose.material3.OutlinedCard
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
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.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.hideFromAccessibility
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.revanced.manager.BuildConfig import app.revanced.manager.BuildConfig
import app.revanced.manager.R import app.revanced.manager.R
@ -42,6 +52,7 @@ import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.settings.SettingsListItem import app.revanced.manager.ui.component.settings.SettingsListItem
import app.revanced.manager.ui.model.navigation.Settings import app.revanced.manager.ui.model.navigation.Settings
import app.revanced.manager.ui.viewmodel.AboutViewModel import app.revanced.manager.ui.viewmodel.AboutViewModel
import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.DEVELOPER_OPTIONS_TAPS
import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.getSocialIcon import app.revanced.manager.ui.viewmodel.AboutViewModel.Companion.getSocialIcon
import app.revanced.manager.util.openUrl import app.revanced.manager.util.openUrl
import app.revanced.manager.util.toast import app.revanced.manager.util.toast
@ -109,7 +120,8 @@ fun AboutSettingsScreen(
} }
val listItems = listOfNotNull( val listItems = listOfNotNull(
Triple(stringResource(R.string.submit_feedback), Triple(
stringResource(R.string.submit_feedback),
stringResource(R.string.submit_feedback_description), stringResource(R.string.submit_feedback_description),
third = { third = {
context.openUrl("https://github.com/ReVanced/revanced-manager/issues/new/choose") context.openUrl("https://github.com/ReVanced/revanced-manager/issues/new/choose")
@ -134,6 +146,35 @@ fun AboutSettingsScreen(
) )
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val snackbarHostState = remember { SnackbarHostState() }
val showDeveloperSettings by viewModel.showDeveloperSettings.getAsState()
var developerTaps by rememberSaveable { mutableIntStateOf(0) }
LaunchedEffect(developerTaps) {
if (developerTaps == 0) return@LaunchedEffect
if (showDeveloperSettings) {
snackbarHostState.showSnackbar(context.getString(R.string.developer_options_already_enabled))
developerTaps = 0
return@LaunchedEffect
}
val remaining = DEVELOPER_OPTIONS_TAPS - developerTaps
if (remaining > 0) {
snackbarHostState.showSnackbar(
context.getString(
R.string.developer_options_taps,
remaining
),
duration = SnackbarDuration.Long
)
} else if (remaining == 0) {
viewModel.showDeveloperSettings.update(true)
snackbarHostState.showSnackbar(context.getString(R.string.developer_options_enabled))
}
// Reset the counter
developerTaps = 0
}
Scaffold( Scaffold(
topBar = { topBar = {
@ -143,6 +184,9 @@ fun AboutSettingsScreen(
onBackClick = onBackClick onBackClick = onBackClick
) )
}, },
snackbarHost = {
SnackbarHost(hostState = snackbarHostState)
},
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { paddingValues -> ) { paddingValues ->
ColumnWithScrollbar( ColumnWithScrollbar(
@ -153,9 +197,11 @@ fun AboutSettingsScreen(
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
Image( Image(
modifier = Modifier.padding(top = 16.dp), modifier = Modifier
.padding(top = 16.dp)
.clickable { developerTaps += 1 },
painter = icon, painter = icon,
contentDescription = null contentDescription = stringResource(R.string.app_name)
) )
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
@ -163,7 +209,11 @@ fun AboutSettingsScreen(
) { ) {
Text( Text(
stringResource(R.string.app_name), stringResource(R.string.app_name),
style = MaterialTheme.typography.headlineSmall style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.semantics {
// Icon already has this information for the purpose of being clickable.
hideFromAccessibility()
}
) )
Text( Text(
text = stringResource(R.string.version) + " " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")", text = stringResource(R.string.version) + " " + BuildConfig.VERSION_NAME + " (" + BuildConfig.VERSION_CODE + ")",

View File

@ -12,11 +12,14 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.GroupHeader import app.revanced.manager.ui.component.GroupHeader
import app.revanced.manager.ui.component.settings.BooleanItem
import app.revanced.manager.ui.component.settings.SettingsListItem import app.revanced.manager.ui.component.settings.SettingsListItem
import app.revanced.manager.ui.viewmodel.DeveloperOptionsViewModel import app.revanced.manager.ui.viewmodel.DeveloperOptionsViewModel
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
import org.koin.compose.koinInject
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -25,6 +28,7 @@ fun DeveloperSettingsScreen(
vm: DeveloperOptionsViewModel = koinViewModel() vm: DeveloperOptionsViewModel = koinViewModel()
) { ) {
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState()) val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val prefs: PreferencesManager = koinInject()
Scaffold( Scaffold(
topBar = { topBar = {
@ -37,6 +41,13 @@ fun DeveloperSettingsScreen(
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { paddingValues -> ) { paddingValues ->
Column(modifier = Modifier.padding(paddingValues)) { Column(modifier = Modifier.padding(paddingValues)) {
GroupHeader(stringResource(R.string.manager))
BooleanItem(
preference = prefs.showDeveloperSettings,
headline = R.string.developer_options,
description = R.string.developer_options_description,
)
GroupHeader(stringResource(R.string.patch_bundles_section)) GroupHeader(stringResource(R.string.patch_bundles_section))
SettingsListItem( SettingsListItem(
headlineContent = stringResource(R.string.patch_bundles_force_download), headlineContent = stringResource(R.string.patch_bundles_force_download),

View File

@ -8,6 +8,7 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.data.platform.NetworkInfo
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.network.api.ReVancedAPI import app.revanced.manager.network.api.ReVancedAPI
import app.revanced.manager.network.dto.ReVancedDonationLink import app.revanced.manager.network.dto.ReVancedDonationLink
import app.revanced.manager.network.dto.ReVancedSocial import app.revanced.manager.network.dto.ReVancedSocial
@ -27,6 +28,7 @@ import kotlinx.coroutines.withContext
class AboutViewModel( class AboutViewModel(
private val reVancedAPI: ReVancedAPI, private val reVancedAPI: ReVancedAPI,
private val network: NetworkInfo, private val network: NetworkInfo,
prefs: PreferencesManager,
) : ViewModel() { ) : ViewModel() {
var socials by mutableStateOf(emptyList<ReVancedSocial>()) var socials by mutableStateOf(emptyList<ReVancedSocial>())
private set private set
@ -37,6 +39,8 @@ class AboutViewModel(
val isConnected: Boolean val isConnected: Boolean
get() = network.isConnected() get() = network.isConnected()
val showDeveloperSettings = prefs.showDeveloperSettings
init { init {
viewModelScope.launch { viewModelScope.launch {
if (!isConnected) { if (!isConnected) {
@ -53,6 +57,8 @@ class AboutViewModel(
} }
companion object { companion object {
const val DEVELOPER_OPTIONS_TAPS = 5
private val socialIcons = mapOf( private val socialIcons = mapOf(
"Discord" to FontAwesomeIcons.Brands.Discord, "Discord" to FontAwesomeIcons.Brands.Discord,
"GitHub" to FontAwesomeIcons.Brands.Github, "GitHub" to FontAwesomeIcons.Brands.Github,

View File

@ -348,6 +348,9 @@
<string name="about_revanced_manager">About ReVanced Manager</string> <string name="about_revanced_manager">About ReVanced Manager</string>
<string name="revanced_manager_description">ReVanced Manager is an application designed to work with ReVanced Patcher, which allows for long-lasting patches to be created for Android apps. The patching system is designed to automatically work with new versions of apps with minimal maintenance.</string> <string name="revanced_manager_description">ReVanced Manager is an application designed to work with ReVanced Patcher, which allows for long-lasting patches to be created for Android apps. The patching system is designed to automatically work with new versions of apps with minimal maintenance.</string>
<string name="developer_options_taps">%d taps remaining</string>
<string name="developer_options_enabled">Developer options enabled</string>
<string name="developer_options_already_enabled">Developer options are already enabled</string>
<string name="update_available">An update is available</string> <string name="update_available">An update is available</string>
<string name="current_version">Current version: %s</string> <string name="current_version">Current version: %s</string>
<string name="new_version">New version: %s</string> <string name="new_version">New version: %s</string>