mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 05:54:26 +02:00
feat: Settings v2
Signed-off-by: validcube <pun.butrach@gmail.com>
This commit is contained in:
parent
e10e5e4e3f
commit
c012a3e9c0
@ -47,7 +47,6 @@ android {
|
|||||||
|
|
||||||
isPseudoLocalesEnabled = true
|
isPseudoLocalesEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
buildConfigField("long", "BUILD_ID", "0L")
|
buildConfigField("long", "BUILD_ID", "0L")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ import app.revanced.manager.ui.screen.settings.ContributorScreen
|
|||||||
import app.revanced.manager.ui.screen.settings.DeveloperOptionsScreen
|
import app.revanced.manager.ui.screen.settings.DeveloperOptionsScreen
|
||||||
import app.revanced.manager.ui.screen.settings.DownloadsSettingsScreen
|
import app.revanced.manager.ui.screen.settings.DownloadsSettingsScreen
|
||||||
import app.revanced.manager.ui.screen.settings.GeneralSettingsScreen
|
import app.revanced.manager.ui.screen.settings.GeneralSettingsScreen
|
||||||
import app.revanced.manager.ui.screen.settings.ImportExportSettingsScreen
|
import app.revanced.manager.ui.screen.settings.BackupRestoreSettingsScreen
|
||||||
import app.revanced.manager.ui.screen.settings.LicensesScreen
|
import app.revanced.manager.ui.screen.settings.LicensesScreen
|
||||||
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
|
import app.revanced.manager.ui.screen.settings.update.ChangelogsScreen
|
||||||
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
|
import app.revanced.manager.ui.screen.settings.update.UpdatesSettingsScreen
|
||||||
@ -290,7 +290,7 @@ private fun ReVancedManager(vm: MainViewModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
composable<Settings.ImportExport> {
|
composable<Settings.ImportExport> {
|
||||||
ImportExportSettingsScreen(onBackClick = navController::popBackStack)
|
BackupRestoreSettingsScreen(onBackClick = navController::popBackStack)
|
||||||
}
|
}
|
||||||
|
|
||||||
composable<Settings.About> {
|
composable<Settings.About> {
|
||||||
|
@ -22,10 +22,9 @@ class PreferencesManager(
|
|||||||
val managerAutoUpdates = booleanPreference("manager_auto_updates", false)
|
val managerAutoUpdates = booleanPreference("manager_auto_updates", false)
|
||||||
val showManagerUpdateDialogOnLaunch = booleanPreference("show_manager_update_dialog_on_launch", true)
|
val showManagerUpdateDialogOnLaunch = booleanPreference("show_manager_update_dialog_on_launch", true)
|
||||||
|
|
||||||
val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false)
|
val allowIncompatibleMixing = booleanPreference("allow_incompatible_mixing", false)
|
||||||
val disableSelectionWarning = booleanPreference("disable_selection_warning", false)
|
val allowChangingPatchSelection = booleanPreference("allow_changing_patch_selection", false)
|
||||||
val disableUniversalPatchWarning = booleanPreference("disable_universal_patch_warning", false)
|
val allowUniversalPatch = booleanPreference("allow_universal_patch", false)
|
||||||
val suggestedVersionSafeguard = booleanPreference("suggested_version_safeguard", true)
|
|
||||||
|
|
||||||
val acknowledgedDownloaderPlugins = stringSetPreference("acknowledged_downloader_plugins", emptySet())
|
val acknowledgedDownloaderPlugins = stringSetPreference("acknowledged_downloader_plugins", emptySet())
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,7 @@ class PatchBundleRepository(
|
|||||||
|
|
||||||
suspend fun isVersionAllowed(packageName: String, version: String) =
|
suspend fun isVersionAllowed(packageName: String, version: String) =
|
||||||
withContext(Dispatchers.Default) {
|
withContext(Dispatchers.Default) {
|
||||||
if (!prefs.suggestedVersionSafeguard.get()) return@withContext true
|
if (!prefs.allowIncompatibleMixing.get()) return@withContext true
|
||||||
|
|
||||||
val suggestedVersion = suggestedVersions.first()[packageName] ?: return@withContext true
|
val suggestedVersion = suggestedVersions.first()[packageName] ?: return@withContext true
|
||||||
suggestedVersion == version
|
suggestedVersion == version
|
||||||
|
@ -66,7 +66,7 @@ fun SelectedAppInfoScreen(
|
|||||||
val version = vm.selectedApp.version
|
val version = vm.selectedApp.version
|
||||||
val bundles by vm.bundleInfoFlow.collectAsStateWithLifecycle(emptyList())
|
val bundles by vm.bundleInfoFlow.collectAsStateWithLifecycle(emptyList())
|
||||||
|
|
||||||
val allowIncompatiblePatches by vm.prefs.disablePatchVersionCompatCheck.getAsState()
|
val allowIncompatiblePatches by vm.prefs.allowIncompatibleMixing.getAsState()
|
||||||
val patches = remember(bundles, allowIncompatiblePatches) {
|
val patches = remember(bundles, allowIncompatiblePatches) {
|
||||||
vm.getPatches(bundles, allowIncompatiblePatches)
|
vm.getPatches(bundles, allowIncompatiblePatches)
|
||||||
}
|
}
|
||||||
|
@ -22,18 +22,13 @@ private val settingsSections = listOf(
|
|||||||
Icons.Outlined.Settings
|
Icons.Outlined.Settings
|
||||||
) to Settings.General,
|
) to Settings.General,
|
||||||
Triple(
|
Triple(
|
||||||
R.string.updates,
|
R.string.extensions,
|
||||||
R.string.updates_description,
|
R.string.extensions_description,
|
||||||
Icons.Outlined.Update
|
|
||||||
) to Settings.Updates,
|
|
||||||
Triple(
|
|
||||||
R.string.downloads,
|
|
||||||
R.string.downloads_description,
|
|
||||||
Icons.Outlined.Download
|
Icons.Outlined.Download
|
||||||
) to Settings.Downloads,
|
) to Settings.Downloads,
|
||||||
Triple(
|
Triple(
|
||||||
R.string.import_export,
|
R.string.backup_restore,
|
||||||
R.string.import_export_description,
|
R.string.backup_restore_description,
|
||||||
Icons.Outlined.SwapVert
|
Icons.Outlined.SwapVert
|
||||||
) to Settings.ImportExport,
|
) to Settings.ImportExport,
|
||||||
Triple(
|
Triple(
|
||||||
|
@ -51,8 +51,9 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
@Composable
|
@Composable
|
||||||
fun AboutSettingsScreen(
|
fun AboutSettingsScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
|
onChangelogClick: () -> Unit,
|
||||||
navigate: (Settings.Destination) -> Unit,
|
navigate: (Settings.Destination) -> Unit,
|
||||||
viewModel: AboutViewModel = koinViewModel()
|
vm: AboutViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
// painterResource() is broken on release builds for some reason.
|
// painterResource() is broken on release builds for some reason.
|
||||||
@ -60,11 +61,11 @@ fun AboutSettingsScreen(
|
|||||||
AppCompatResources.getDrawable(context, R.drawable.ic_logo_ring)
|
AppCompatResources.getDrawable(context, R.drawable.ic_logo_ring)
|
||||||
})
|
})
|
||||||
|
|
||||||
val (preferredSocials, socials) = remember(viewModel.socials) {
|
val (preferredSocials, socials) = remember(vm.socials) {
|
||||||
viewModel.socials.partition(ReVancedSocial::preferred)
|
vm.socials.partition(ReVancedSocial::preferred)
|
||||||
}
|
}
|
||||||
|
|
||||||
val preferredSocialButtons = remember(preferredSocials, viewModel.donate, viewModel.contact) {
|
val preferredSocialButtons = remember(preferredSocials, vm.donate, vm.contact) {
|
||||||
preferredSocials.map {
|
preferredSocials.map {
|
||||||
Triple(
|
Triple(
|
||||||
getSocialIcon(it.name),
|
getSocialIcon(it.name),
|
||||||
@ -74,7 +75,7 @@ fun AboutSettingsScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
} + listOfNotNull(
|
} + listOfNotNull(
|
||||||
viewModel.donate?.let {
|
vm.donate?.let {
|
||||||
Triple(
|
Triple(
|
||||||
Icons.Outlined.FavoriteBorder,
|
Icons.Outlined.FavoriteBorder,
|
||||||
context.getString(R.string.donate),
|
context.getString(R.string.donate),
|
||||||
@ -83,7 +84,7 @@ fun AboutSettingsScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
viewModel.contact?.let {
|
vm.contact?.let {
|
||||||
Triple(
|
Triple(
|
||||||
Icons.Outlined.MailOutline,
|
Icons.Outlined.MailOutline,
|
||||||
context.getString(R.string.contact),
|
context.getString(R.string.contact),
|
||||||
@ -108,11 +109,18 @@ fun AboutSettingsScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val listItems = listOfNotNull(
|
val listItems = listOfNotNull(
|
||||||
Triple(stringResource(R.string.submit_feedback),
|
Triple(
|
||||||
|
stringResource(R.string.changelog),
|
||||||
|
stringResource(R.string.changelog_description),
|
||||||
|
third = { onChangelogClick }
|
||||||
|
),
|
||||||
|
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")
|
||||||
}),
|
},
|
||||||
|
),
|
||||||
Triple(
|
Triple(
|
||||||
stringResource(R.string.contributors),
|
stringResource(R.string.contributors),
|
||||||
stringResource(R.string.contributors_description),
|
stringResource(R.string.contributors_description),
|
||||||
|
@ -17,6 +17,7 @@ import androidx.compose.foundation.layout.fillMaxWidth
|
|||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.outlined.Api
|
import androidx.compose.material.icons.outlined.Api
|
||||||
|
import androidx.compose.material.icons.outlined.Edit
|
||||||
import androidx.compose.material.icons.outlined.Restore
|
import androidx.compose.material.icons.outlined.Restore
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@ -89,73 +90,81 @@ fun AdvancedSettingsScreen(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
) {
|
) {
|
||||||
GroupHeader(stringResource(R.string.manager))
|
val apiSource by vm.prefs.api.getAsState()
|
||||||
|
var showApiSourceDialog by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
val apiUrl by vm.prefs.api.getAsState()
|
if (showApiSourceDialog) {
|
||||||
var showApiUrlDialog by rememberSaveable { mutableStateOf(false) }
|
APISourceDialog(
|
||||||
|
currentUrl = apiSource,
|
||||||
if (showApiUrlDialog) {
|
|
||||||
APIUrlDialog(
|
|
||||||
currentUrl = apiUrl,
|
|
||||||
defaultUrl = vm.prefs.api.default,
|
defaultUrl = vm.prefs.api.default,
|
||||||
onSubmit = {
|
onSubmit = {
|
||||||
showApiUrlDialog = false
|
showApiSourceDialog = false
|
||||||
it?.let(vm::setApiUrl)
|
it?.let(vm::setApiSource)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SettingsListItem(
|
|
||||||
headlineContent = stringResource(R.string.api_url),
|
|
||||||
supportingContent = stringResource(R.string.api_url_description),
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
showApiUrlDialog = true
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.patcher))
|
GroupHeader(stringResource(R.string.revanced_patcher))
|
||||||
BooleanItem(
|
|
||||||
preference = vm.prefs.useProcessRuntime,
|
|
||||||
coroutineScope = vm.viewModelScope,
|
|
||||||
headline = R.string.process_runtime,
|
|
||||||
description = R.string.process_runtime_description,
|
|
||||||
)
|
|
||||||
IntegerItem(
|
IntegerItem(
|
||||||
preference = vm.prefs.patcherProcessMemoryLimit,
|
preference = vm.prefs.patcherProcessMemoryLimit,
|
||||||
coroutineScope = vm.viewModelScope,
|
coroutineScope = vm.viewModelScope,
|
||||||
headline = R.string.process_runtime_memory_limit,
|
headline = R.string.process_runtime_memory_limit,
|
||||||
description = R.string.process_runtime_memory_limit_description,
|
description = vm.prefs.patcherProcessMemoryLimit.getAsState().value,
|
||||||
)
|
)
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.safeguards))
|
GroupHeader(stringResource(R.string.manager))
|
||||||
SafeguardBooleanItem(
|
SettingsListItem(
|
||||||
preference = vm.prefs.disablePatchVersionCompatCheck,
|
headlineContent = stringResource(R.string.api_source),
|
||||||
coroutineScope = vm.viewModelScope,
|
supportingContent = apiSource,
|
||||||
headline = R.string.patch_compat_check,
|
modifier = Modifier.clickable {
|
||||||
description = R.string.patch_compat_check_description,
|
showApiSourceDialog = true
|
||||||
confirmationText = R.string.patch_compat_check_confirmation
|
},
|
||||||
|
trailingContent = {
|
||||||
|
IconButton(onClick = { showApiSourceDialog = true }) {
|
||||||
|
Icon(
|
||||||
|
Icons.Outlined.Edit,
|
||||||
|
contentDescription = stringResource(R.string.edit)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
SafeguardBooleanItem(
|
SafeguardBooleanItem(
|
||||||
preference = vm.prefs.disableUniversalPatchWarning,
|
preference = vm.prefs.allowIncompatibleMixing,
|
||||||
|
coroutineScope = vm.viewModelScope,
|
||||||
|
headline = R.string.allow_compatibility_mixing,
|
||||||
|
description = R.string.allow_compatibility_mixing_description,
|
||||||
|
confirmationText = R.string.allow_compatibility_mixing_confirmation
|
||||||
|
)
|
||||||
|
SafeguardBooleanItem(
|
||||||
|
preference = vm.prefs.allowUniversalPatch,
|
||||||
coroutineScope = vm.viewModelScope,
|
coroutineScope = vm.viewModelScope,
|
||||||
headline = R.string.universal_patches_safeguard,
|
headline = R.string.universal_patches_safeguard,
|
||||||
description = R.string.universal_patches_safeguard_description,
|
description = R.string.universal_patches_safeguard_description,
|
||||||
confirmationText = R.string.universal_patches_safeguard_confirmation
|
confirmationText = R.string.universal_patches_safeguard_confirmation
|
||||||
)
|
)
|
||||||
SafeguardBooleanItem(
|
SafeguardBooleanItem(
|
||||||
preference = vm.prefs.suggestedVersionSafeguard,
|
preference = vm.prefs.allowChangingPatchSelection,
|
||||||
coroutineScope = vm.viewModelScope,
|
|
||||||
headline = R.string.suggested_version_safeguard,
|
|
||||||
description = R.string.suggested_version_safeguard_description,
|
|
||||||
confirmationText = R.string.suggested_version_safeguard_confirmation
|
|
||||||
)
|
|
||||||
SafeguardBooleanItem(
|
|
||||||
preference = vm.prefs.disableSelectionWarning,
|
|
||||||
coroutineScope = vm.viewModelScope,
|
coroutineScope = vm.viewModelScope,
|
||||||
headline = R.string.patch_selection_safeguard,
|
headline = R.string.patch_selection_safeguard,
|
||||||
description = R.string.patch_selection_safeguard_description,
|
description = R.string.patch_selection_safeguard_description,
|
||||||
confirmationText = R.string.patch_selection_safeguard_confirmation
|
confirmationText = R.string.patch_selection_safeguard_confirmation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
GroupHeader(stringResource(R.string.update))
|
||||||
|
BooleanItem(
|
||||||
|
preference = vm.showManagerUpdateDialogOnLaunch,
|
||||||
|
headline = R.string.show_manager_update_dialog_on_launch,
|
||||||
|
description = R.string.check_for_update_auto_description
|
||||||
|
)
|
||||||
|
|
||||||
|
GroupHeader(stringResource(R.string.experimental_features))
|
||||||
|
BooleanItem(
|
||||||
|
preference = vm.prefs.useProcessRuntime,
|
||||||
|
coroutineScope = vm.viewModelScope,
|
||||||
|
headline = R.string.process_runtime,
|
||||||
|
description = R.string.process_runtime_description,
|
||||||
|
)
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.debugging))
|
GroupHeader(stringResource(R.string.debugging))
|
||||||
val exportDebugLogsLauncher =
|
val exportDebugLogsLauncher =
|
||||||
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {
|
rememberLauncherForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) {
|
||||||
@ -167,11 +176,10 @@ fun AdvancedSettingsScreen(
|
|||||||
)
|
)
|
||||||
val clipboard = remember { context.getSystemService<ClipboardManager>()!! }
|
val clipboard = remember { context.getSystemService<ClipboardManager>()!! }
|
||||||
val deviceContent = """
|
val deviceContent = """
|
||||||
Version: ${BuildConfig.VERSION_NAME} (${BuildConfig.VERSION_CODE})
|
Version: ${BuildConfig.VERSION_NAME}, ${BuildConfig.BUILD_TYPE} (${BuildConfig.VERSION_CODE})
|
||||||
Build type: ${BuildConfig.BUILD_TYPE}
|
|
||||||
Model: ${Build.MODEL}
|
Model: ${Build.MODEL}
|
||||||
Android version: ${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})
|
Android version: ${Build.VERSION.RELEASE} (${Build.VERSION.SDK_INT})
|
||||||
Supported Archs: ${Build.SUPPORTED_ABIS.joinToString(", ")}
|
Preferred Architectures: ${Build.SUPPORTED_ABIS.joinToString(", ")}
|
||||||
Memory limit: $memoryLimit
|
Memory limit: $memoryLimit
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
SettingsListItem(
|
SettingsListItem(
|
||||||
@ -194,18 +202,18 @@ fun AdvancedSettingsScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun APIUrlDialog(currentUrl: String, defaultUrl: String, onSubmit: (String?) -> Unit) {
|
private fun APISourceDialog(currentUrl: String, defaultUrl: String, onSubmit: (String?) -> Unit) {
|
||||||
var url by rememberSaveable(currentUrl) { mutableStateOf(currentUrl) }
|
var source by rememberSaveable(currentUrl) { mutableStateOf(currentUrl) }
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { onSubmit(null) },
|
onDismissRequest = { onSubmit(null) },
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onSubmit(url)
|
onSubmit(source)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.api_url_dialog_save))
|
Text(stringResource(R.string.api_source_dialog_save))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
@ -218,7 +226,7 @@ private fun APIUrlDialog(currentUrl: String, defaultUrl: String, onSubmit: (Stri
|
|||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.api_url_dialog_title),
|
text = stringResource(R.string.api_source_dialog_title),
|
||||||
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
@ -228,23 +236,23 @@ private fun APIUrlDialog(currentUrl: String, defaultUrl: String, onSubmit: (Stri
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.api_url_dialog_description),
|
text = stringResource(R.string.api_source_dialog_description),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.api_url_dialog_warning),
|
text = stringResource(R.string.api_source_dialog_warning),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.error
|
color = MaterialTheme.colorScheme.error
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
value = url,
|
value = source,
|
||||||
onValueChange = { url = it },
|
onValueChange = { source = it },
|
||||||
label = { Text(stringResource(R.string.api_url)) },
|
label = { Text(stringResource(R.string.api_source)) },
|
||||||
trailingIcon = {
|
trailingIcon = {
|
||||||
IconButton(onClick = { url = defaultUrl }) {
|
IconButton(onClick = { source = defaultUrl }) {
|
||||||
Icon(Icons.Outlined.Restore, stringResource(R.string.api_url_dialog_reset))
|
Icon(Icons.Outlined.Restore, stringResource(R.string.api_source_dialog_reset))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -32,6 +32,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
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.graphics.Color
|
||||||
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
|
||||||
@ -54,7 +55,7 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun ImportExportSettingsScreen(
|
fun BackupRestoreSettingsScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
vm: ImportExportViewModel = koinViewModel()
|
vm: ImportExportViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
@ -100,7 +101,7 @@ fun ImportExportSettingsScreen(
|
|||||||
vm.viewModelScope.launch {
|
vm.viewModelScope.launch {
|
||||||
uiSafe(context, R.string.failed_to_import_keystore, "Failed to import keystore") {
|
uiSafe(context, R.string.failed_to_import_keystore, "Failed to import keystore") {
|
||||||
val result = vm.tryKeystoreImport(cn, pass)
|
val result = vm.tryKeystoreImport(cn, pass)
|
||||||
if (!result) context.toast(context.getString(R.string.import_keystore_wrong_credentials))
|
if (!result) context.toast(context.getString(R.string.restore_keystore_wrong_credentials))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,7 +113,7 @@ fun ImportExportSettingsScreen(
|
|||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppTopBar(
|
AppTopBar(
|
||||||
title = stringResource(R.string.import_export),
|
title = stringResource(R.string.backup_restore),
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
onBackClick = onBackClick
|
onBackClick = onBackClick
|
||||||
)
|
)
|
||||||
@ -147,63 +148,89 @@ fun ImportExportSettingsScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.import_))
|
GroupHeader(stringResource(R.string.keystore))
|
||||||
GroupItem(
|
|
||||||
onClick = {
|
|
||||||
importKeystoreLauncher.launch("*/*")
|
|
||||||
},
|
|
||||||
headline = R.string.import_keystore,
|
|
||||||
description = R.string.import_keystore_description
|
|
||||||
)
|
|
||||||
GroupItem(
|
|
||||||
onClick = vm::importSelection,
|
|
||||||
headline = R.string.import_patch_selection,
|
|
||||||
description = R.string.import_patch_selection_description
|
|
||||||
)
|
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.export))
|
|
||||||
GroupItem(
|
GroupItem(
|
||||||
onClick = {
|
onClick = {
|
||||||
if (!vm.canExport()) {
|
if (!vm.canExport()) {
|
||||||
context.toast(context.getString(R.string.export_keystore_unavailable))
|
context.toast(context.getString(R.string.backup_keystore_unavailable))
|
||||||
return@GroupItem
|
return@GroupItem
|
||||||
}
|
}
|
||||||
exportKeystoreLauncher.launch("Manager.keystore")
|
exportKeystoreLauncher.launch("Manager.keystore")
|
||||||
},
|
},
|
||||||
headline = R.string.export_keystore,
|
headline = R.string.backup,
|
||||||
description = R.string.export_keystore_description
|
description = R.string.backup_keystore_description
|
||||||
)
|
)
|
||||||
GroupItem(
|
GroupItem(
|
||||||
onClick = vm::exportSelection,
|
onClick = {
|
||||||
headline = R.string.export_patch_selection,
|
importKeystoreLauncher.launch("*/*")
|
||||||
description = R.string.export_patch_selection_description
|
},
|
||||||
|
headline = R.string.restore,
|
||||||
|
description = R.string.restore_keystore_description
|
||||||
)
|
)
|
||||||
|
|
||||||
GroupHeader(stringResource(R.string.reset))
|
|
||||||
GroupItem(
|
GroupItem(
|
||||||
onClick = vm::regenerateKeystore,
|
onClick = vm::regenerateKeystore,
|
||||||
headline = R.string.regenerate_keystore,
|
headline = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.regenerate_keystore),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
},
|
||||||
description = R.string.regenerate_keystore_description
|
description = R.string.regenerate_keystore_description
|
||||||
)
|
)
|
||||||
|
|
||||||
|
GroupHeader(stringResource(R.string.patch_selection))
|
||||||
|
GroupItem(
|
||||||
|
onClick = vm::exportSelection,
|
||||||
|
headline = R.string.backup,
|
||||||
|
description = R.string.restore_patch_selection_description
|
||||||
|
)
|
||||||
|
GroupItem(
|
||||||
|
onClick = vm::importSelection,
|
||||||
|
headline = R.string.restore,
|
||||||
|
description = R.string.backup_patch_selection_description
|
||||||
|
)
|
||||||
GroupItem(
|
GroupItem(
|
||||||
onClick = vm::resetSelection, // TODO: allow resetting selection for specific bundle or package name.
|
onClick = vm::resetSelection, // TODO: allow resetting selection for specific bundle or package name.
|
||||||
headline = R.string.reset_patch_selection,
|
headline = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.reset),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
},
|
||||||
description = R.string.reset_patch_selection_description
|
description = R.string.reset_patch_selection_description
|
||||||
)
|
)
|
||||||
|
|
||||||
|
GroupHeader(stringResource(R.string.patch_options))
|
||||||
|
// TODO: patch options import/export.
|
||||||
GroupItem(
|
GroupItem(
|
||||||
onClick = vm::resetOptions, // TODO: patch options import/export.
|
onClick = vm::resetOptions,
|
||||||
headline = R.string.patch_options_reset_all,
|
headline = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.reset),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
},
|
||||||
description = R.string.patch_options_reset_all_description,
|
description = R.string.patch_options_reset_all_description,
|
||||||
)
|
)
|
||||||
GroupItem(
|
GroupItem(
|
||||||
onClick = { showPackageSelector = true },
|
onClick = { showPackageSelector = true },
|
||||||
headline = R.string.patch_options_reset_package,
|
headline = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.patch_options_reset_package),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
},
|
||||||
description = R.string.patch_options_reset_package_description
|
description = R.string.patch_options_reset_package_description
|
||||||
)
|
)
|
||||||
if (patchBundles.size > 1) {
|
if (patchBundles.size > 1) {
|
||||||
GroupItem(
|
GroupItem(
|
||||||
onClick = { showBundleSelector = true },
|
onClick = { showBundleSelector = true },
|
||||||
headline = R.string.patch_options_reset_bundle,
|
headline = {
|
||||||
|
Text(
|
||||||
|
stringResource(R.string.patch_options_reset_bundle),
|
||||||
|
color = MaterialTheme.colorScheme.error
|
||||||
|
)
|
||||||
|
},
|
||||||
description = R.string.patch_options_reset_bundle_description,
|
description = R.string.patch_options_reset_bundle_description,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -282,6 +309,19 @@ private fun GroupItem(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun GroupItem(
|
||||||
|
onClick: () -> Unit,
|
||||||
|
headline: @Composable () -> Unit,
|
||||||
|
@StringRes description: Int? = null
|
||||||
|
) {
|
||||||
|
SettingsListItem(
|
||||||
|
modifier = Modifier.clickable { onClick() },
|
||||||
|
headlineContent = headline,
|
||||||
|
supportingContent = description?.let { stringResource(it) }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun KeystoreCredentialsDialog(
|
fun KeystoreCredentialsDialog(
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
@ -298,7 +338,7 @@ fun KeystoreCredentialsDialog(
|
|||||||
onSubmit(cn, pass)
|
onSubmit(cn, pass)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.import_keystore_dialog_button))
|
Text(stringResource(R.string.restore_keystore_dialog_button))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
@ -311,7 +351,7 @@ fun KeystoreCredentialsDialog(
|
|||||||
},
|
},
|
||||||
title = {
|
title = {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.import_keystore_dialog_title),
|
text = stringResource(R.string.restore_keystore_dialog_title),
|
||||||
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
)
|
)
|
||||||
@ -322,19 +362,19 @@ fun KeystoreCredentialsDialog(
|
|||||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.import_keystore_dialog_description),
|
text = stringResource(R.string.restore_keystore_dialog_description),
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = cn,
|
value = cn,
|
||||||
onValueChange = { cn = it },
|
onValueChange = { cn = it },
|
||||||
label = { Text(stringResource(R.string.import_keystore_dialog_alias_field)) }
|
label = { Text(stringResource(R.string.restore_keystore_dialog_alias_field)) }
|
||||||
)
|
)
|
||||||
PasswordField(
|
PasswordField(
|
||||||
value = pass,
|
value = pass,
|
||||||
onValueChange = { pass = it },
|
onValueChange = { pass = it },
|
||||||
label = { Text(stringResource(R.string.import_keystore_dialog_password_field)) }
|
label = { Text(stringResource(R.string.restore_keystore_dialog_password_field)) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -53,9 +53,9 @@ import org.koin.androidx.compose.koinViewModel
|
|||||||
@Composable
|
@Composable
|
||||||
fun ContributorScreen(
|
fun ContributorScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
viewModel: ContributorViewModel = koinViewModel()
|
vm: ContributorViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
val repositories = viewModel.repositories
|
val repositories = vm.repositories
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
@ -44,7 +44,13 @@ fun DeveloperOptionsScreen(
|
|||||||
)
|
)
|
||||||
SettingsListItem(
|
SettingsListItem(
|
||||||
headlineContent = stringResource(R.string.patch_bundles_reset),
|
headlineContent = stringResource(R.string.patch_bundles_reset),
|
||||||
modifier = Modifier.clickable(onClick = vm::redownloadBundles)
|
modifier = Modifier.clickable(onClick = vm::resetBundles)
|
||||||
|
)
|
||||||
|
|
||||||
|
GroupHeader(stringResource(R.string.testing))
|
||||||
|
SettingsListItem(
|
||||||
|
headlineContent = stringResource(R.string.disable_safeguard),
|
||||||
|
modifier = Modifier.clickable(onClick = vm::disableSafeguard)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -53,22 +53,22 @@ import java.security.MessageDigest
|
|||||||
@Composable
|
@Composable
|
||||||
fun DownloadsSettingsScreen(
|
fun DownloadsSettingsScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
viewModel: DownloadsViewModel = koinViewModel()
|
vm: DownloadsViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
val pullRefreshState = rememberPullToRefreshState()
|
val pullRefreshState = rememberPullToRefreshState()
|
||||||
val downloadedApps by viewModel.downloadedApps.collectAsStateWithLifecycle(emptyList())
|
val downloadedApps by vm.downloadedApps.collectAsStateWithLifecycle(emptyList())
|
||||||
val pluginStates by viewModel.downloaderPluginStates.collectAsStateWithLifecycle()
|
val pluginStates by vm.downloaderPluginStates.collectAsStateWithLifecycle()
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppTopBar(
|
AppTopBar(
|
||||||
title = stringResource(R.string.downloads),
|
title = stringResource(R.string.extensions),
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
onBackClick = onBackClick,
|
onBackClick = onBackClick,
|
||||||
actions = {
|
actions = {
|
||||||
if (viewModel.appSelection.isNotEmpty()) {
|
if (vm.appSelection.isNotEmpty()) {
|
||||||
IconButton(onClick = { viewModel.deleteApps() }) {
|
IconButton(onClick = { vm.deleteApps() }) {
|
||||||
Icon(Icons.Default.Delete, stringResource(R.string.delete))
|
Icon(Icons.Default.Delete, stringResource(R.string.delete))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ fun DownloadsSettingsScreen(
|
|||||||
) {
|
) {
|
||||||
PullToRefreshDefaults.Indicator(
|
PullToRefreshDefaults.Indicator(
|
||||||
state = pullRefreshState,
|
state = pullRefreshState,
|
||||||
isRefreshing = viewModel.isRefreshingPlugins
|
isRefreshing = vm.isRefreshingPlugins
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,9 +95,9 @@ fun DownloadsSettingsScreen(
|
|||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(paddingValues)
|
.padding(paddingValues)
|
||||||
.pullToRefresh(
|
.pullToRefresh(
|
||||||
isRefreshing = viewModel.isRefreshingPlugins,
|
isRefreshing = vm.isRefreshingPlugins,
|
||||||
state = pullRefreshState,
|
state = pullRefreshState,
|
||||||
onRefresh = viewModel::refreshPlugins
|
onRefresh = vm::refreshPlugins
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
item {
|
item {
|
||||||
@ -115,7 +115,7 @@ fun DownloadsSettingsScreen(
|
|||||||
|
|
||||||
val packageInfo =
|
val packageInfo =
|
||||||
remember(packageName) {
|
remember(packageName) {
|
||||||
viewModel.pm.getPackageInfo(
|
vm.pm.getPackageInfo(
|
||||||
packageName
|
packageName
|
||||||
)
|
)
|
||||||
} ?: return@item
|
} ?: return@item
|
||||||
@ -124,7 +124,7 @@ fun DownloadsSettingsScreen(
|
|||||||
val signature =
|
val signature =
|
||||||
remember(packageName) {
|
remember(packageName) {
|
||||||
val androidSignature =
|
val androidSignature =
|
||||||
viewModel.pm.getSignature(packageName)
|
vm.pm.getSignature(packageName)
|
||||||
val hash = MessageDigest.getInstance("SHA-256")
|
val hash = MessageDigest.getInstance("SHA-256")
|
||||||
.digest(androidSignature.toByteArray())
|
.digest(androidSignature.toByteArray())
|
||||||
hash.toHexString(format = HexFormat.UpperCase)
|
hash.toHexString(format = HexFormat.UpperCase)
|
||||||
@ -140,7 +140,7 @@ fun DownloadsSettingsScreen(
|
|||||||
),
|
),
|
||||||
onDismiss = ::dismiss,
|
onDismiss = ::dismiss,
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
viewModel.revokePluginTrust(packageName)
|
vm.revokePluginTrust(packageName)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -161,7 +161,7 @@ fun DownloadsSettingsScreen(
|
|||||||
),
|
),
|
||||||
onDismiss = ::dismiss,
|
onDismiss = ::dismiss,
|
||||||
onConfirm = {
|
onConfirm = {
|
||||||
viewModel.trustPlugin(packageName)
|
vm.trustPlugin(packageName)
|
||||||
dismiss()
|
dismiss()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -201,17 +201,17 @@ fun DownloadsSettingsScreen(
|
|||||||
GroupHeader(stringResource(R.string.downloaded_apps))
|
GroupHeader(stringResource(R.string.downloaded_apps))
|
||||||
}
|
}
|
||||||
items(downloadedApps, key = { it.packageName to it.version }) { app ->
|
items(downloadedApps, key = { it.packageName to it.version }) { app ->
|
||||||
val selected = app in viewModel.appSelection
|
val selected = app in vm.appSelection
|
||||||
|
|
||||||
SettingsListItem(
|
SettingsListItem(
|
||||||
modifier = Modifier.clickable { viewModel.toggleApp(app) },
|
modifier = Modifier.clickable { vm.toggleApp(app) },
|
||||||
headlineContent = app.packageName,
|
headlineContent = app.packageName,
|
||||||
leadingContent = (@Composable {
|
leadingContent = (@Composable {
|
||||||
HapticCheckbox(
|
HapticCheckbox(
|
||||||
checked = selected,
|
checked = selected,
|
||||||
onCheckedChange = { viewModel.toggleApp(app) }
|
onCheckedChange = { vm.toggleApp(app) }
|
||||||
)
|
)
|
||||||
}).takeIf { viewModel.appSelection.isNotEmpty() },
|
}).takeIf { vm.appSelection.isNotEmpty() },
|
||||||
supportingContent = app.version,
|
supportingContent = app.version,
|
||||||
tonalElevation = if (selected) 8.dp else 0.dp
|
tonalElevation = if (selected) 8.dp else 0.dp
|
||||||
)
|
)
|
||||||
|
@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.material3.AlertDialog
|
||||||
|
import androidx.compose.material3.Button
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@ -35,6 +36,7 @@ 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.theme.Theme
|
import app.revanced.manager.ui.theme.Theme
|
||||||
import app.revanced.manager.ui.viewmodel.GeneralSettingsViewModel
|
import app.revanced.manager.ui.viewmodel.GeneralSettingsViewModel
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
import org.koin.compose.koinInject
|
import org.koin.compose.koinInject
|
||||||
|
|
||||||
@ -42,16 +44,17 @@ import org.koin.compose.koinInject
|
|||||||
@Composable
|
@Composable
|
||||||
fun GeneralSettingsScreen(
|
fun GeneralSettingsScreen(
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
viewModel: GeneralSettingsViewModel = koinViewModel()
|
vm: GeneralSettingsViewModel = koinViewModel(),
|
||||||
|
onUpdateClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val prefs = viewModel.prefs
|
val prefs = vm.prefs
|
||||||
val coroutineScope = viewModel.viewModelScope
|
val coroutineScope = vm.viewModelScope
|
||||||
var showThemePicker by rememberSaveable { mutableStateOf(false) }
|
var showThemePicker by rememberSaveable { mutableStateOf(false) }
|
||||||
|
|
||||||
if (showThemePicker) {
|
if (showThemePicker) {
|
||||||
ThemePicker(
|
ThemePicker(
|
||||||
onDismiss = { showThemePicker = false },
|
onDismiss = { showThemePicker = false },
|
||||||
onConfirm = { viewModel.setTheme(it) }
|
onConfirm = { vm.setTheme(it) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||||
@ -76,10 +79,10 @@ fun GeneralSettingsScreen(
|
|||||||
val theme by prefs.theme.getAsState()
|
val theme by prefs.theme.getAsState()
|
||||||
SettingsListItem(
|
SettingsListItem(
|
||||||
modifier = Modifier.clickable { showThemePicker = true },
|
modifier = Modifier.clickable { showThemePicker = true },
|
||||||
headlineContent = stringResource(R.string.theme),
|
headlineContent = stringResource(R.string.theme_mode),
|
||||||
supportingContent = stringResource(R.string.theme_description),
|
supportingContent = stringResource(R.string.theme_mode_description),
|
||||||
trailingContent = {
|
trailingContent = {
|
||||||
FilledTonalButton(
|
Button (
|
||||||
onClick = {
|
onClick = {
|
||||||
showThemePicker = true
|
showThemePicker = true
|
||||||
}
|
}
|
||||||
@ -92,10 +95,23 @@ fun GeneralSettingsScreen(
|
|||||||
BooleanItem(
|
BooleanItem(
|
||||||
preference = prefs.dynamicColor,
|
preference = prefs.dynamicColor,
|
||||||
coroutineScope = coroutineScope,
|
coroutineScope = coroutineScope,
|
||||||
headline = R.string.dynamic_color,
|
headline = R.string.personalized_color,
|
||||||
description = R.string.dynamic_color_description
|
description = R.string.personalized_color_description
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GroupHeader(stringResource(R.string.update))
|
||||||
|
BooleanItem(
|
||||||
|
preference = vm.managerAutoUpdates,
|
||||||
|
headline = R.string.check_for_update,
|
||||||
|
description = R.string.check_for_update_auto_description
|
||||||
|
)
|
||||||
|
FilledTonalButton (
|
||||||
|
modifier = Modifier.padding(top = paddingValues.calculateTopPadding()),
|
||||||
|
onClick = onUpdateClick
|
||||||
|
) {
|
||||||
|
Text(stringResource(R.string.check_for_update))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -110,7 +126,7 @@ private fun ThemePicker(
|
|||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
title = { Text(stringResource(R.string.theme)) },
|
title = { Text(stringResource(R.string.theme_mode)) },
|
||||||
text = {
|
text = {
|
||||||
Column {
|
Column {
|
||||||
Theme.entries.forEach {
|
Theme.entries.forEach {
|
||||||
|
@ -1,81 +0,0 @@
|
|||||||
package app.revanced.manager.ui.screen.settings.update
|
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
|
||||||
import androidx.compose.material3.Scaffold
|
|
||||||
import androidx.compose.material3.TopAppBarDefaults
|
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
|
||||||
import androidx.compose.ui.res.stringResource
|
|
||||||
import app.revanced.manager.R
|
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
|
||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
|
||||||
import app.revanced.manager.ui.component.settings.BooleanItem
|
|
||||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
|
||||||
import app.revanced.manager.ui.viewmodel.UpdatesSettingsViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
|
||||||
import org.koin.androidx.compose.koinViewModel
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun UpdatesSettingsScreen(
|
|
||||||
onBackClick: () -> Unit,
|
|
||||||
onChangelogClick: () -> Unit,
|
|
||||||
onUpdateClick: () -> Unit,
|
|
||||||
vm: UpdatesSettingsViewModel = koinViewModel(),
|
|
||||||
) {
|
|
||||||
val coroutineScope = rememberCoroutineScope()
|
|
||||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
|
||||||
|
|
||||||
Scaffold(
|
|
||||||
topBar = {
|
|
||||||
AppTopBar(
|
|
||||||
title = stringResource(R.string.updates),
|
|
||||||
scrollBehavior = scrollBehavior,
|
|
||||||
onBackClick = onBackClick
|
|
||||||
)
|
|
||||||
},
|
|
||||||
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
|
|
||||||
) { paddingValues ->
|
|
||||||
ColumnWithScrollbar(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.padding(paddingValues)
|
|
||||||
) {
|
|
||||||
SettingsListItem(
|
|
||||||
modifier = Modifier.clickable {
|
|
||||||
coroutineScope.launch {
|
|
||||||
if (vm.checkForUpdates()) onUpdateClick()
|
|
||||||
}
|
|
||||||
},
|
|
||||||
headlineContent = stringResource(R.string.manual_update_check),
|
|
||||||
supportingContent = stringResource(R.string.manual_update_check_description)
|
|
||||||
)
|
|
||||||
|
|
||||||
SettingsListItem(
|
|
||||||
modifier = Modifier.clickable(onClick = onChangelogClick),
|
|
||||||
headlineContent = stringResource(R.string.changelog),
|
|
||||||
supportingContent = stringResource(
|
|
||||||
R.string.changelog_description
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
BooleanItem(
|
|
||||||
preference = vm.managerAutoUpdates,
|
|
||||||
headline = R.string.update_checking_manager,
|
|
||||||
description = R.string.update_checking_manager_description
|
|
||||||
)
|
|
||||||
|
|
||||||
BooleanItem(
|
|
||||||
preference = vm.showManagerUpdateDialogOnLaunch,
|
|
||||||
headline = R.string.show_manager_update_dialog_on_launch,
|
|
||||||
description = R.string.update_checking_manager_description
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -27,6 +27,7 @@ class AdvancedSettingsViewModel(
|
|||||||
private val app: Application,
|
private val app: Application,
|
||||||
private val patchBundleRepository: PatchBundleRepository
|
private val patchBundleRepository: PatchBundleRepository
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
val showManagerUpdateDialogOnLaunch = prefs.showManagerUpdateDialogOnLaunch
|
||||||
val debugLogFileName: String
|
val debugLogFileName: String
|
||||||
get() {
|
get() {
|
||||||
val time = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())
|
val time = DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(LocalDateTime.now())
|
||||||
@ -34,7 +35,7 @@ class AdvancedSettingsViewModel(
|
|||||||
return "revanced-manager_logcat_$time"
|
return "revanced-manager_logcat_$time"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun setApiUrl(value: String) = viewModelScope.launch(Dispatchers.Default) {
|
fun setApiSource(value: String) = viewModelScope.launch(Dispatchers.Default) {
|
||||||
if (value == prefs.api.get()) return@launch
|
if (value == prefs.api.get()) return@launch
|
||||||
|
|
||||||
prefs.api.update(value)
|
prefs.api.update(value)
|
||||||
|
@ -24,4 +24,10 @@ class DeveloperOptionsViewModel(
|
|||||||
fun resetBundles() = viewModelScope.launch {
|
fun resetBundles() = viewModelScope.launch {
|
||||||
patchBundleRepository.reset()
|
patchBundleRepository.reset()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun disableSafeguard() = viewModelScope.launch {
|
||||||
|
prefs.allowIncompatibleMixing.update(true)
|
||||||
|
prefs.allowChangingPatchSelection.update(true)
|
||||||
|
prefs.allowUniversalPatch.update(true)
|
||||||
|
}
|
||||||
}
|
}
|
@ -1,15 +1,38 @@
|
|||||||
package app.revanced.manager.ui.viewmodel
|
package app.revanced.manager.ui.viewmodel
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.domain.manager.PreferencesManager
|
import app.revanced.manager.domain.manager.PreferencesManager
|
||||||
|
import app.revanced.manager.network.api.ReVancedAPI
|
||||||
import app.revanced.manager.ui.theme.Theme
|
import app.revanced.manager.ui.theme.Theme
|
||||||
|
import app.revanced.manager.util.toast
|
||||||
|
import app.revanced.manager.util.uiSafe
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
class GeneralSettingsViewModel(
|
class GeneralSettingsViewModel(
|
||||||
val prefs: PreferencesManager
|
val prefs: PreferencesManager,
|
||||||
|
private val app: Application,
|
||||||
|
private val reVancedAPI: ReVancedAPI,
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
fun setTheme(theme: Theme) = viewModelScope.launch {
|
fun setTheme(theme: Theme) = viewModelScope.launch {
|
||||||
prefs.theme.update(theme)
|
prefs.theme.update(theme)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val managerAutoUpdates = prefs.managerAutoUpdates
|
||||||
|
val showManagerUpdateDialogOnLaunch = prefs.showManagerUpdateDialogOnLaunch
|
||||||
|
|
||||||
|
suspend fun checkForUpdates(): Boolean {
|
||||||
|
uiSafe(app, R.string.failed_to_check_updates, "Failed to check for updates") {
|
||||||
|
app.toast(app.getString(R.string.update_check))
|
||||||
|
|
||||||
|
if (reVancedAPI.getAppUpdate() == null)
|
||||||
|
app.toast(app.getString(R.string.no_update_available))
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
@ -103,7 +103,7 @@ class ImportExportViewModel(
|
|||||||
private suspend fun tryKeystoreImport(cn: String, pass: String, path: Path): Boolean {
|
private suspend fun tryKeystoreImport(cn: String, pass: String, path: Path): Boolean {
|
||||||
path.inputStream().use { stream ->
|
path.inputStream().use { stream ->
|
||||||
if (keystoreManager.import(cn, pass, stream)) {
|
if (keystoreManager.import(cn, pass, stream)) {
|
||||||
app.toast(app.getString(R.string.import_keystore_success))
|
app.toast(app.getString(R.string.restore_keystore_success))
|
||||||
cancelKeystoreImport()
|
cancelKeystoreImport()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -122,7 +122,7 @@ class ImportExportViewModel(
|
|||||||
|
|
||||||
fun exportKeystore(target: Uri) = viewModelScope.launch {
|
fun exportKeystore(target: Uri) = viewModelScope.launch {
|
||||||
keystoreManager.export(contentResolver.openOutputStream(target)!!)
|
keystoreManager.export(contentResolver.openOutputStream(target)!!)
|
||||||
app.toast(app.getString(R.string.export_keystore_success))
|
app.toast(app.getString(R.string.backup_keystore_success))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun regenerateKeystore() = viewModelScope.launch {
|
fun regenerateKeystore() = viewModelScope.launch {
|
||||||
@ -171,7 +171,7 @@ class ImportExportViewModel(
|
|||||||
override val activityArg = JSON_MIMETYPE
|
override val activityArg = JSON_MIMETYPE
|
||||||
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
|
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
|
||||||
app,
|
app,
|
||||||
R.string.import_patch_selection_fail,
|
R.string.restore_patch_selection_fail,
|
||||||
"Failed to restore patch selection"
|
"Failed to restore patch selection"
|
||||||
) {
|
) {
|
||||||
val selection = withContext(Dispatchers.IO) {
|
val selection = withContext(Dispatchers.IO) {
|
||||||
@ -181,7 +181,7 @@ class ImportExportViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
selectionRepository.import(bundleUid, selection)
|
selectionRepository.import(bundleUid, selection)
|
||||||
app.toast(app.getString(R.string.import_patch_selection_success))
|
app.toast(app.getString(R.string.restore_patch_selection_success))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -190,7 +190,7 @@ class ImportExportViewModel(
|
|||||||
override val activityArg = "selection.json"
|
override val activityArg = "selection.json"
|
||||||
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
|
override suspend fun execute(bundleUid: Int, location: Uri) = uiSafe(
|
||||||
app,
|
app,
|
||||||
R.string.export_patch_selection_fail,
|
R.string.backup_patch_selection_fail,
|
||||||
"Failed to backup patch selection"
|
"Failed to backup patch selection"
|
||||||
) {
|
) {
|
||||||
val selection = selectionRepository.export(bundleUid)
|
val selection = selectionRepository.export(bundleUid)
|
||||||
@ -200,7 +200,7 @@ class ImportExportViewModel(
|
|||||||
Json.Default.encodeToStream(selection, it)
|
Json.Default.encodeToStream(selection, it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
app.toast(app.getString(R.string.export_patch_selection_success))
|
app.toast(app.getString(R.string.backup_patch_selection_success))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -124,7 +124,7 @@ class MainViewModel(
|
|||||||
prefs.api.update(api.removeSuffix("/"))
|
prefs.api.update(api.removeSuffix("/"))
|
||||||
}
|
}
|
||||||
settings.experimentalPatchesEnabled?.let { allowExperimental ->
|
settings.experimentalPatchesEnabled?.let { allowExperimental ->
|
||||||
prefs.disablePatchVersionCompatCheck.update(allowExperimental)
|
prefs.allowIncompatibleMixing.update(allowExperimental)
|
||||||
}
|
}
|
||||||
settings.patchesAutoUpdate?.let { autoUpdate ->
|
settings.patchesAutoUpdate?.let { autoUpdate ->
|
||||||
with(patchBundleRepository) {
|
with(patchBundleRepository) {
|
||||||
|
@ -58,13 +58,13 @@ class PatchesSelectorViewModel(input: SelectedApplicationInfo.PatchesSelector.Vi
|
|||||||
private set
|
private set
|
||||||
|
|
||||||
val allowIncompatiblePatches =
|
val allowIncompatiblePatches =
|
||||||
get<PreferencesManager>().disablePatchVersionCompatCheck.getBlocking()
|
get<PreferencesManager>().allowIncompatibleMixing.getBlocking()
|
||||||
val bundlesFlow =
|
val bundlesFlow =
|
||||||
get<PatchBundleRepository>().bundleInfoFlow(packageName, input.app.version)
|
get<PatchBundleRepository>().bundleInfoFlow(packageName, input.app.version)
|
||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
universalPatchWarningEnabled = !prefs.disableUniversalPatchWarning.get()
|
universalPatchWarningEnabled = !prefs.allowUniversalPatch.get()
|
||||||
|
|
||||||
if (prefs.disableSelectionWarning.get()) {
|
if (prefs.disableSelectionWarning.get()) {
|
||||||
selectionWarningEnabled = false
|
selectionWarningEnabled = false
|
||||||
|
@ -30,7 +30,6 @@ import app.revanced.manager.domain.repository.PatchOptionsRepository
|
|||||||
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
import app.revanced.manager.domain.repository.PatchSelectionRepository
|
||||||
import app.revanced.manager.network.downloader.LoadedDownloaderPlugin
|
import app.revanced.manager.network.downloader.LoadedDownloaderPlugin
|
||||||
import app.revanced.manager.network.downloader.ParceledDownloaderData
|
import app.revanced.manager.network.downloader.ParceledDownloaderData
|
||||||
import app.revanced.manager.patcher.patch.PatchInfo
|
|
||||||
import app.revanced.manager.plugin.downloader.GetScope
|
import app.revanced.manager.plugin.downloader.GetScope
|
||||||
import app.revanced.manager.plugin.downloader.PluginHostApi
|
import app.revanced.manager.plugin.downloader.PluginHostApi
|
||||||
import app.revanced.manager.plugin.downloader.UserInteractionException
|
import app.revanced.manager.plugin.downloader.UserInteractionException
|
||||||
@ -118,7 +117,7 @@ class SelectedAppInfoViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val requiredVersion = combine(
|
val requiredVersion = combine(
|
||||||
prefs.suggestedVersionSafeguard.flow,
|
prefs.allowIncompatibleMixing.flow,
|
||||||
bundleRepository.suggestedVersions
|
bundleRepository.suggestedVersions
|
||||||
) { suggestedVersionSafeguard, suggestedVersions ->
|
) { suggestedVersionSafeguard, suggestedVersions ->
|
||||||
if (!suggestedVersionSafeguard) return@combine null
|
if (!suggestedVersionSafeguard) return@combine null
|
||||||
@ -275,7 +274,7 @@ class SelectedAppInfoViewModel(
|
|||||||
)
|
)
|
||||||
|
|
||||||
suspend fun getPatcherParams(): Patcher.ViewModelParams {
|
suspend fun getPatcherParams(): Patcher.ViewModelParams {
|
||||||
val allowUnsupported = prefs.disablePatchVersionCompatCheck.get()
|
val allowUnsupported = prefs.allowIncompatibleMixing.get()
|
||||||
val bundles = bundleInfoFlow.first()
|
val bundles = bundleInfoFlow.first()
|
||||||
return Patcher.ViewModelParams(
|
return Patcher.ViewModelParams(
|
||||||
selectedApp,
|
selectedApp,
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
<string name="cli">CLI</string>
|
<string name="cli">CLI</string>
|
||||||
<string name="manager">Manager</string>
|
<string name="manager">Manager</string>
|
||||||
|
|
||||||
|
<string name="revanced_patcher">ReVanced Patcher</string>
|
||||||
|
|
||||||
<string name="plugin_host_permission_label">ReVanced Manager plugin host</string>
|
<string name="plugin_host_permission_label">ReVanced Manager plugin host</string>
|
||||||
<string name="plugin_host_permission_description">Used to control access to ReVanced Manager plugins. Only ReVanced Manager has this.</string>
|
<string name="plugin_host_permission_description">Used to control access to ReVanced Manager plugins. Only ReVanced Manager has this.</string>
|
||||||
|
|
||||||
@ -66,71 +68,73 @@
|
|||||||
<string name="auto_updates_dialog_note">These settings can be changed later.</string>
|
<string name="auto_updates_dialog_note">These settings can be changed later.</string>
|
||||||
|
|
||||||
<string name="general">General</string>
|
<string name="general">General</string>
|
||||||
<string name="general_description">Theme, dynamic color</string>
|
<string name="general_description">Appearances, Updates</string>
|
||||||
|
|
||||||
<string name="updates">Updates</string>
|
<string name="updates">Updates</string>
|
||||||
<string name="updates_description">Check for updates and view changelogs</string>
|
<string name="updates_description">Check for updates and view changelogs</string>
|
||||||
<string name="downloads">Downloads</string>
|
|
||||||
<string name="downloads_description">Downloader plugins and downloaded apps</string>
|
<string name="extensions">Extensions</string>
|
||||||
<string name="import_export">Import & export</string>
|
<string name="extensions_description">Downloader plugins, downloaded apps</string>
|
||||||
<string name="import_export_description">Keystore, patch options and selection</string>
|
<string name="backup_restore">Backup & Restore</string>
|
||||||
|
<string name="backup_restore_description">Keystore, Patch selections, Patch options</string>
|
||||||
<string name="advanced">Advanced</string>
|
<string name="advanced">Advanced</string>
|
||||||
<string name="advanced_description">API URL, memory limit, debugging</string>
|
<string name="advanced_description">API Source, memory limits, debug logs</string>
|
||||||
|
<string name="experimental_features">Experimental features</string>
|
||||||
|
|
||||||
|
|
||||||
<string name="about">About</string>
|
<string name="about">About</string>
|
||||||
<string name="opensource_licenses">Open source licenses</string>
|
<string name="opensource_licenses">Open source licenses</string>
|
||||||
<string name="opensource_licenses_description">View all the libraries used to make this application</string>
|
<string name="opensource_licenses_description">View all the libraries used to make this application</string>
|
||||||
|
|
||||||
<string name="contributors">Contributors</string>
|
<string name="contributors">Contributors</string>
|
||||||
<string name="contributors_description">View the contributors of ReVanced</string>
|
<string name="contributors_description">View the contributors of ReVanced</string>
|
||||||
<string name="dynamic_color">Dynamic color</string>
|
<string name="personalized_color">Personalized color</string>
|
||||||
<string name="dynamic_color_description">Adapt colors to the wallpaper</string>
|
<string name="personalized_color_description">Use color provided by your device\'s palette</string>
|
||||||
<string name="theme">Theme</string>
|
<string name="theme_mode">Theme mode</string>
|
||||||
<string name="theme_description">Choose between light or dark theme</string>
|
<string name="theme_mode_description">Choose between light, dark, and system provided mode</string>
|
||||||
<string name="safeguards">Safeguards</string>
|
<string name="safeguards">Safeguards</string>
|
||||||
<string name="patch_compat_check">Disable version compatibility check</string>
|
<string name="allow_compatibility_mixing">Allow unsupported compatibility</string>
|
||||||
<string name="patch_compat_check_description">The check restricts patches to supported app versions</string>
|
<string name="allow_compatibility_mixing_description">Permit apps and patches to be mixed in unsupported state</string>
|
||||||
<string name="patch_compat_check_confirmation">Selecting incompatible patches can result in a broken app.\n\nDo you want to proceed anyways?</string>
|
<string name="allow_compatibility_mixing_confirmation">Selecting incompatible patches can result in a broken app.\n\nAllow anyways?</string>
|
||||||
<string name="suggested_version_safeguard">Require suggested app version</string>
|
|
||||||
<string name="suggested_version_safeguard_description">Enforce selection of the suggested app version</string>
|
|
||||||
<string name="suggested_version_safeguard_confirmation">Selecting an app that is not the suggested version may cause unexpected issues.\n\nDo you want to proceed anyways?</string>
|
|
||||||
<string name="patch_selection_safeguard">Allow changing patch selection</string>
|
<string name="patch_selection_safeguard">Allow changing patch selection</string>
|
||||||
<string name="patch_selection_safeguard_description">Do not prevent selecting or deselecting patches</string>
|
<string name="patch_selection_safeguard_description">Permit selecting or deselecting patches from default</string>
|
||||||
<string name="patch_selection_safeguard_confirmation">Changing the selection of patches may cause unexpected issues.\n\nEnable anyways?</string>
|
<string name="patch_selection_safeguard_confirmation">Changing the selection of patches may cause unexpected issues.\n\nAllow anyways?</string>
|
||||||
<string name="universal_patches_safeguard">Disable universal patch warning</string>
|
<string name="universal_patches_safeguard">Allow universal patch</string>
|
||||||
<string name="universal_patches_safeguard_description">Disables the warning that appears when you try to select universal patches</string>
|
<string name="universal_patches_safeguard_description">Permit selecting app\'s generic patch</string>
|
||||||
<string name="universal_patches_safeguard_confirmation">Universal patches are not as well tested as those that target specific apps.\n\nEnable anyways?</string>
|
<string name="universal_patches_safeguard_confirmation">Universal patch are not as well tested as those that target specific apps.\n\nAllow anyways?</string>
|
||||||
<string name="import_keystore">Import keystore</string>
|
<string name="backup">Backup</string>
|
||||||
<string name="import_keystore_description">Import a custom keystore</string>
|
<string name="restore">Restore</string>
|
||||||
<string name="import_keystore_dialog_title">Enter keystore credentials</string>
|
<string name="keystore">Keystore</string>
|
||||||
<string name="import_keystore_dialog_description">You\'ll need enter the keystore’s credentials to import it.</string>
|
<string name="patch_selection">Patch selection</string>
|
||||||
<string name="import_keystore_dialog_alias_field">Username (Alias)</string>
|
<string name="patch_options">Patch options</string>
|
||||||
<string name="import_keystore_dialog_password_field">Password</string>
|
<string name="restore_keystore_description">Restore keystore from external source</string>
|
||||||
<string name="import_keystore_dialog_button">Import</string>
|
<string name="restore_keystore_dialog_title">Enter keystore credentials</string>
|
||||||
<string name="import_keystore_wrong_credentials">Wrong keystore credentials</string>
|
<string name="restore_keystore_dialog_description">You\'ll need enter the keystore’s credentials to restore it.</string>
|
||||||
<string name="import_keystore_success">Imported keystore</string>
|
<string name="restore_keystore_dialog_alias_field">Username (Alias)</string>
|
||||||
<string name="export_keystore">Export keystore</string>
|
<string name="restore_keystore_dialog_password_field">Password</string>
|
||||||
<string name="export_keystore_description">Export the current keystore</string>
|
<string name="restore_keystore_dialog_button">Import</string>
|
||||||
<string name="export_keystore_unavailable">No keystore to export</string>
|
<string name="restore_keystore_wrong_credentials">Wrong keystore credentials</string>
|
||||||
<string name="export_keystore_success">Exported keystore</string>
|
<string name="restore_keystore_success">Keystore successfully restored</string>
|
||||||
<string name="regenerate_keystore">Regenerate keystore</string>
|
<string name="backup_keystore_description">Export app’s keystore into usable file</string>
|
||||||
<string name="regenerate_keystore_description">Generate a new keystore</string>
|
<string name="backup_keystore_unavailable">No keystore to backup</string>
|
||||||
|
<string name="backup_keystore_success">Keystore successfully backed up</string>
|
||||||
|
<string name="regenerate_keystore">Regeneration</string>
|
||||||
|
<string name="regenerate_keystore_description">Replace current keystore with new one</string>
|
||||||
<string name="regenerate_keystore_success">The keystore has been successfully replaced</string>
|
<string name="regenerate_keystore_success">The keystore has been successfully replaced</string>
|
||||||
<string name="import_patch_selection">Import patch selection</string>
|
<string name="restore_patch_selection_description">Export app’s patch selections into usable file</string>
|
||||||
<string name="import_patch_selection_description">Import patch selection from a JSON file</string>
|
<string name="restore_patch_selection_fail">Could not import patch selection: %s</string>
|
||||||
<string name="import_patch_selection_fail">Could not import patch selection: %s</string>
|
<string name="restore_patch_selection_success">Patch selection successfully restored</string>
|
||||||
<string name="import_patch_selection_success">Imported patch selection</string>
|
<string name="backup_patch_selection_description">Import app’s patch selections from external source</string>
|
||||||
<string name="export_patch_selection">Export patch selection</string>
|
<string name="backup_patch_selection_fail">Could not backup patch selection: %s</string>
|
||||||
<string name="export_patch_selection_description">Export patch selection to a JSON file</string>
|
<string name="backup_patch_selection_success">Patch selection successfully restored</string>
|
||||||
<string name="export_patch_selection_fail">Could not export patch selection: %s</string>
|
|
||||||
<string name="export_patch_selection_success">Exported patch selection</string>
|
|
||||||
<string name="reset_patch_selection">Reset patch selection</string>
|
<string name="reset_patch_selection">Reset patch selection</string>
|
||||||
<string name="reset_patch_selection_description">Reset the stored patch selection</string>
|
<string name="reset_patch_selection_description">Reset the stored patch selection</string>
|
||||||
<string name="reset_patch_selection_success">Patch selection has been reset</string>
|
<string name="reset_patch_selection_success">Patch selection successfully reset</string>
|
||||||
<string name="patch_options_reset_package">Reset patch options for app</string>
|
<string name="patch_options_reset_package">Reset for app</string>
|
||||||
<string name="patch_options_reset_package_description">Resets patch options for a single app</string>
|
<string name="patch_options_reset_package_description">Use the default patch options configuration for a single app</string>
|
||||||
<string name="patch_options_reset_bundle">Resets patch options for bundle</string>
|
<string name="patch_options_reset_bundle">Reset for bundle</string>
|
||||||
<string name="patch_options_reset_bundle_description">Resets patch options for all patches in a bundle</string>
|
<string name="patch_options_reset_bundle_description">Use the default patch options configuration for all patches in a bundle</string>
|
||||||
<string name="patch_options_reset_all">Reset patch options</string>
|
<string name="patch_options_reset_all_description">Use the default patch options configuration</string>
|
||||||
<string name="patch_options_reset_all_description">Resets all patch options</string>
|
|
||||||
<string name="downloader_plugins">Plugins</string>
|
<string name="downloader_plugins">Plugins</string>
|
||||||
<string name="downloader_plugin_state_trusted">Trusted</string>
|
<string name="downloader_plugin_state_trusted">Trusted</string>
|
||||||
<string name="downloader_plugin_state_failed">Failed to load. Click for more details</string>
|
<string name="downloader_plugin_state_failed">Failed to load. Click for more details</string>
|
||||||
@ -170,21 +174,22 @@
|
|||||||
<string name="dark">Dark</string>
|
<string name="dark">Dark</string>
|
||||||
<string name="appearance">Appearance</string>
|
<string name="appearance">Appearance</string>
|
||||||
<string name="downloaded_apps">Downloaded apps</string>
|
<string name="downloaded_apps">Downloaded apps</string>
|
||||||
<string name="process_runtime">Run Patcher in another process (experimental)</string>
|
<string name="process_runtime">Run patcher in another process</string>
|
||||||
<string name="process_runtime_description">This is faster and allows Patcher to use more memory.</string>
|
<string name="process_runtime_description">Faster and allows patcher to use more memory</string>
|
||||||
<string name="process_runtime_memory_limit">Patcher process memory limit</string>
|
<string name="process_runtime_memory_limit">Patcher process memory limit</string>
|
||||||
<string name="process_runtime_memory_limit_description">The max amount of memory that the Patcher process can use (in megabytes)</string>
|
<string name="process_runtime_memory_limit_description">The max amount of memory that the patcher process can use (in megabytes)</string>
|
||||||
<string name="debug_logs_export">Export debug logs</string>
|
<string name="debug_logs_export">Export debug logs</string>
|
||||||
<string name="debug_logs_export_read_failed">Failed to read logs (exit code %d)</string>
|
<string name="debug_logs_export_read_failed">Failed to read logs (exit code %d)</string>
|
||||||
<string name="debug_logs_export_failed">Failed to export logs</string>
|
<string name="debug_logs_export_failed">Failed to export logs</string>
|
||||||
<string name="debug_logs_export_success">Exported logs</string>
|
<string name="debug_logs_export_success">Exported logs</string>
|
||||||
<string name="api_url">API URL</string>
|
<string name="api_source">API Source</string>
|
||||||
<string name="api_url_description">The API used to download necessary files.</string>
|
<string name="api_source_dialog_title">Set custom API Source</string>
|
||||||
<string name="api_url_dialog_title">Set custom API URL</string>
|
<string name="api_source_dialog_description">Set the API Source of ReVanced Manager. ReVanced Manager uses the API to download patches and updates.</string>
|
||||||
<string name="api_url_dialog_description">Set the API URL of ReVanced Manager. ReVanced Manager uses the API to download patches and updates.</string>
|
<string name="api_source_dialog_warning">ReVanced Manager connects to the API to download patches and updates. Make sure that you trust it.</string>
|
||||||
<string name="api_url_dialog_warning">ReVanced Manager connects to the API to download patches and updates. Make sure that you trust it.</string>
|
<string name="api_source_dialog_save">Set</string>
|
||||||
<string name="api_url_dialog_save">Set</string>
|
<string name="api_source_dialog_reset">Reset API Source</string>
|
||||||
<string name="api_url_dialog_reset">Reset API URL</string>
|
<string name="testing">Testing</string>
|
||||||
|
<string name="disable_safeguard">Disable all safeguards</string>
|
||||||
<string name="device">Device</string>
|
<string name="device">Device</string>
|
||||||
<string name="device_android_version">Android version</string>
|
<string name="device_android_version">Android version</string>
|
||||||
<string name="device_model">Model</string>
|
<string name="device_model">Model</string>
|
||||||
@ -341,10 +346,8 @@
|
|||||||
<string name="ready_to_install_update">Ready to install update</string>
|
<string name="ready_to_install_update">Ready to install update</string>
|
||||||
<string name="update_completed">Update installed</string>
|
<string name="update_completed">Update installed</string>
|
||||||
<string name="install_update_manager_failed">Failed to install update</string>
|
<string name="install_update_manager_failed">Failed to install update</string>
|
||||||
<string name="manual_update_check">Check for updates</string>
|
<string name="check_for_update">Check for update</string>
|
||||||
<string name="manual_update_check_description">Manually check for updates</string>
|
<string name="check_for_update_auto_description">Automatically check for new version when the app launched</string>
|
||||||
<string name="update_checking_manager">Auto check for updates</string>
|
|
||||||
<string name="update_checking_manager_description">Check for new versions of ReVanced Manager when the application starts</string>
|
|
||||||
<string name="changelog">View changelogs</string>
|
<string name="changelog">View changelogs</string>
|
||||||
<string name="changelog_loading">Loading changelog</string>
|
<string name="changelog_loading">Loading changelog</string>
|
||||||
<string name="changelog_download_fail">Failed to download changelog: %s</string>
|
<string name="changelog_download_fail">Failed to download changelog: %s</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user