mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-11 21:56:17 +00:00
Compare commits
11 Commits
v1.26.0-de
...
feat/root-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff70a77afb | ||
|
|
c436a7a100 | ||
|
|
dbb6c01e89 | ||
|
|
e0d529c2df | ||
|
|
0300da9eac | ||
|
|
4e5057ecad | ||
|
|
1ef5c1c5c5 | ||
|
|
39f52c1242 | ||
|
|
0c62837454 | ||
|
|
3d75ffe6a7 | ||
|
|
d188d5a410 |
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
4
.github/ISSUE_TEMPLATE/feature_request.yml
vendored
@@ -1,3 +1,7 @@
|
||||
name: ⭐ Feature request
|
||||
description: Create a detailed request for a new feature.
|
||||
title: 'feat: '
|
||||
labels: ['Feature request']
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
|
||||
@@ -1,3 +1,24 @@
|
||||
# app [1.26.0-dev.13](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.12...v1.26.0-dev.13) (2025-12-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Make patcher screen design more consistent with inspiration ([#2805](https://github.com/ReVanced/revanced-manager/issues/2805)) ([dbb6c01](https://github.com/ReVanced/revanced-manager/commit/dbb6c01e89a5e710185ff4304de0ac9e19bed053))
|
||||
|
||||
# app [1.26.0-dev.12](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.11...v1.26.0-dev.12) (2025-12-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Improve trust plugin dialog design ([#2420](https://github.com/ReVanced/revanced-manager/issues/2420)) ([0300da9](https://github.com/ReVanced/revanced-manager/commit/0300da9eac6c0fc29dbbb66622c0d52f4cf68934))
|
||||
|
||||
# app [1.26.0-dev.11](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.10...v1.26.0-dev.11) (2025-10-25)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Add pure black theme ([#2824](https://github.com/ReVanced/revanced-manager/issues/2824)) ([3d75ffe](https://github.com/ReVanced/revanced-manager/commit/3d75ffe6a7a39efdebe13dbd07c937c1de409ead))
|
||||
|
||||
# app [1.26.0-dev.10](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.9...v1.26.0-dev.10) (2025-10-06)
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
version = 1.26.0-dev.10
|
||||
version = 1.26.0-dev.13
|
||||
|
||||
@@ -81,6 +81,7 @@ class MainActivity : ComponentActivity() {
|
||||
)
|
||||
val theme by vm.prefs.theme.getAsState()
|
||||
val dynamicColor by vm.prefs.dynamicColor.getAsState()
|
||||
val pureBlackTheme by vm.prefs.pureBlackTheme.getAsState()
|
||||
|
||||
EventEffect(vm.legacyImportActivityFlow) {
|
||||
try {
|
||||
@@ -91,7 +92,8 @@ class MainActivity : ComponentActivity() {
|
||||
|
||||
ReVancedManagerTheme(
|
||||
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
|
||||
dynamicColor = dynamicColor
|
||||
dynamicColor = dynamicColor,
|
||||
pureBlackTheme = pureBlackTheme
|
||||
) {
|
||||
ReVancedManager(vm)
|
||||
}
|
||||
|
||||
@@ -54,7 +54,11 @@ class RootInstaller(
|
||||
await()
|
||||
}
|
||||
|
||||
suspend fun execute(vararg commands: String) = getShell().newJob().add(*commands).exec()
|
||||
suspend fun execute(vararg commands: String): Shell.Result {
|
||||
val stdout = mutableListOf<String>()
|
||||
val stderr = mutableListOf<String>()
|
||||
return getShell().newJob().add(*commands).to(stdout, stderr).exec()
|
||||
}
|
||||
|
||||
fun hasRootAccess() = Shell.isAppGrantedRoot() ?: false
|
||||
|
||||
@@ -108,20 +112,15 @@ class RootInstaller(
|
||||
unmount(packageName)
|
||||
|
||||
stockAPK?.let { stockApp ->
|
||||
pm.getPackageInfo(packageName)?.let { packageInfo ->
|
||||
// TODO: get user id programmatically
|
||||
if (pm.getVersionCode(packageInfo) <= pm.getVersionCode(
|
||||
pm.getPackageInfo(patchedAPK)
|
||||
?: error("Failed to get package info for patched app")
|
||||
)
|
||||
)
|
||||
execute("pm uninstall -k --user 0 $packageName").assertSuccess("Failed to uninstall stock app")
|
||||
}
|
||||
// TODO: get user id programmatically
|
||||
execute("pm uninstall -k --user 0 $packageName")
|
||||
|
||||
execute("pm install \"${stockApp.absolutePath}\"").assertSuccess("Failed to install stock app")
|
||||
execute("pm install -r -d --user 0 \"${stockApp.absolutePath}\"")
|
||||
.assertSuccess("Failed to install stock app")
|
||||
}
|
||||
|
||||
remoteFS.getFile(modulePath).mkdir()
|
||||
remoteFS.getFile(modulePath).mkdirs()
|
||||
.also { if (!it) throw Exception("Failed to create module directory") }
|
||||
|
||||
listOf(
|
||||
"service.sh",
|
||||
@@ -142,7 +141,6 @@ class RootInstaller(
|
||||
}
|
||||
|
||||
"$modulePath/$packageName.apk".let { apkPath ->
|
||||
|
||||
remoteFS.getFile(patchedAPK.absolutePath)
|
||||
.also { if (!it.exists()) throw Exception("File doesn't exist") }
|
||||
.newInputStream().use { inputStream ->
|
||||
@@ -173,9 +171,43 @@ class RootInstaller(
|
||||
const val modulesPath = "/data/adb/modules"
|
||||
|
||||
private fun Shell.Result.assertSuccess(errorMessage: String) {
|
||||
if (!isSuccess) throw Exception(errorMessage)
|
||||
if (!isSuccess) {
|
||||
throw ShellCommandException(
|
||||
errorMessage,
|
||||
code,
|
||||
out,
|
||||
err
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ShellCommandException(
|
||||
val userMessage: String,
|
||||
val exitCode: Int,
|
||||
val stdout: List<String>,
|
||||
val stderr: List<String>
|
||||
) : Exception(format(userMessage, exitCode, stdout, stderr)) {
|
||||
companion object {
|
||||
private fun format(message: String, exitCode: Int, stdout: List<String>, stderr: List<String>): String =
|
||||
buildString {
|
||||
appendLine(message)
|
||||
appendLine("Exit code: $exitCode")
|
||||
|
||||
val output = stdout.filter { it.isNotBlank() }
|
||||
val errors = stderr.filter { it.isNotBlank() }
|
||||
|
||||
if (output.isNotEmpty()) {
|
||||
appendLine("stdout:")
|
||||
output.forEach(::appendLine)
|
||||
}
|
||||
if (errors.isNotEmpty()) {
|
||||
appendLine("stderr:")
|
||||
errors.forEach(::appendLine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RootServiceException : Exception("Root not available")
|
||||
@@ -9,6 +9,7 @@ class PreferencesManager(
|
||||
context: Context
|
||||
) : BasePreferencesManager(context, "settings") {
|
||||
val dynamicColor = booleanPreference("dynamic_color", true)
|
||||
val pureBlackTheme = booleanPreference("pure_black_theme", false)
|
||||
val theme = enumPreference("theme", Theme.SYSTEM)
|
||||
|
||||
val api = stringPreference("api_url", "https://api.revanced.app")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package app.revanced.manager.ui.component.patcher
|
||||
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.Crossfade
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
@@ -12,7 +12,6 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Cancel
|
||||
import androidx.compose.material.icons.filled.CheckCircle
|
||||
@@ -21,6 +20,7 @@ import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
@@ -53,20 +53,11 @@ fun Steps(
|
||||
category: StepCategory,
|
||||
steps: List<Step>,
|
||||
stepCount: Pair<Int, Int>? = null,
|
||||
stepProgressProvider: StepProgressProvider
|
||||
stepProgressProvider: StepProgressProvider,
|
||||
isExpanded: Boolean = false,
|
||||
onExpand: () -> Unit,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
var expanded by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
val categoryColor by animateColorAsState(
|
||||
if (expanded) MaterialTheme.colorScheme.surfaceContainerHigh else Color.Transparent,
|
||||
label = "category"
|
||||
)
|
||||
|
||||
val cardColor by animateColorAsState(
|
||||
if (expanded) MaterialTheme.colorScheme.surfaceContainer else Color.Transparent,
|
||||
label = "card"
|
||||
)
|
||||
|
||||
val state = remember(steps) {
|
||||
when {
|
||||
steps.all { it.state == State.COMPLETED } -> State.COMPLETED
|
||||
@@ -76,48 +67,52 @@ fun Steps(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(state) {
|
||||
if (state == State.RUNNING)
|
||||
onExpand()
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clip(MaterialTheme.shapes.large)
|
||||
.fillMaxWidth()
|
||||
.background(cardColor)
|
||||
.background(MaterialTheme.colorScheme.surfaceContainerLow)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(14.dp),
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.clickable { expanded = !expanded }
|
||||
.background(categoryColor)
|
||||
.clickable(true, onClick = onClick)
|
||||
.fillMaxWidth()
|
||||
.padding(20.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) {
|
||||
StepIcon(state = state, size = 24.dp)
|
||||
StepIcon(state = state, size = 24.dp)
|
||||
|
||||
Text(stringResource(category.displayName))
|
||||
Text(stringResource(category.displayName))
|
||||
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
|
||||
val stepProgress = remember(stepCount, steps) {
|
||||
stepCount?.let { (current, total) -> "$current/$total" }
|
||||
?: "${steps.count { it.state == State.COMPLETED }}/${steps.size}"
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stepProgress,
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
|
||||
ArrowButton(modifier = Modifier.size(24.dp), expanded = expanded, onClick = null)
|
||||
val stepProgress = remember(stepCount, steps) {
|
||||
stepCount?.let { (current, total) -> "$current/$total" }
|
||||
?: "${steps.count { it.state == State.COMPLETED }}/${steps.size}"
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stepProgress,
|
||||
style = MaterialTheme.typography.labelSmall
|
||||
)
|
||||
|
||||
ArrowButton(modifier = Modifier.size(24.dp), expanded = isExpanded, onClick = null)
|
||||
}
|
||||
|
||||
AnimatedVisibility(visible = expanded) {
|
||||
AnimatedVisibility(visible = isExpanded) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
modifier = Modifier
|
||||
.background(MaterialTheme.colorScheme.background.copy(0.6f))
|
||||
.fillMaxWidth()
|
||||
.padding(top = 10.dp)
|
||||
) {
|
||||
steps.forEach { step ->
|
||||
steps.forEachIndexed { index, step ->
|
||||
val (progress, progressText) = when (step.progressKey) {
|
||||
null -> null
|
||||
ProgressKey.DOWNLOAD -> stepProgressProvider.downloadProgress?.let { (downloaded, total) ->
|
||||
@@ -131,7 +126,9 @@ fun Steps(
|
||||
state = step.state,
|
||||
message = step.message,
|
||||
progress = progress,
|
||||
progressText = progressText
|
||||
progressText = progressText,
|
||||
isFirst = index == 0,
|
||||
isLast = index == steps.lastIndex,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -145,7 +142,9 @@ fun SubStep(
|
||||
state: State,
|
||||
message: String? = null,
|
||||
progress: Float? = null,
|
||||
progressText: String? = null
|
||||
progressText: String? = null,
|
||||
isFirst: Boolean = false,
|
||||
isLast: Boolean = false,
|
||||
) {
|
||||
var messageExpanded by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
@@ -156,22 +155,22 @@ fun SubStep(
|
||||
clickable { messageExpanded = !messageExpanded }
|
||||
else this
|
||||
}
|
||||
.padding(top = if (isFirst) 10.dp else 8.dp, bottom = if (isLast) 20.dp else 8.dp)
|
||||
.padding(horizontal = 20.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp),
|
||||
modifier = Modifier.padding(horizontal = 16.dp, vertical = 8.dp)
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier.size(24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
StepIcon(state, progress, size = 20.dp)
|
||||
}
|
||||
StepIcon(
|
||||
size = 18.dp,
|
||||
state = state,
|
||||
progress = progress,
|
||||
)
|
||||
|
||||
Text(
|
||||
text = name,
|
||||
style = MaterialTheme.typography.titleSmall,
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f, true),
|
||||
@@ -201,7 +200,7 @@ fun SubStep(
|
||||
text = message.orEmpty(),
|
||||
style = MaterialTheme.typography.labelMedium,
|
||||
color = MaterialTheme.colorScheme.secondary,
|
||||
modifier = Modifier.padding(horizontal = 52.dp, vertical = 8.dp)
|
||||
modifier = Modifier.padding(horizontal = 36.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -211,40 +210,44 @@ fun SubStep(
|
||||
fun StepIcon(state: State, progress: Float? = null, size: Dp) {
|
||||
val strokeWidth = Dp(floor(size.value / 10) + 1)
|
||||
|
||||
when (state) {
|
||||
State.COMPLETED -> Icon(
|
||||
Icons.Filled.CheckCircle,
|
||||
contentDescription = stringResource(R.string.step_completed),
|
||||
tint = MaterialTheme.colorScheme.surfaceTint,
|
||||
modifier = Modifier.size(size)
|
||||
)
|
||||
|
||||
State.FAILED -> Icon(
|
||||
Icons.Filled.Cancel,
|
||||
contentDescription = stringResource(R.string.step_failed),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.size(size)
|
||||
)
|
||||
|
||||
State.WAITING -> Icon(
|
||||
Icons.Outlined.Circle,
|
||||
contentDescription = stringResource(R.string.step_waiting),
|
||||
tint = MaterialTheme.colorScheme.surfaceVariant,
|
||||
modifier = Modifier.size(size)
|
||||
)
|
||||
|
||||
State.RUNNING ->
|
||||
LoadingIndicator(
|
||||
modifier = stringResource(R.string.step_running).let { description ->
|
||||
Modifier
|
||||
.size(size)
|
||||
.semantics {
|
||||
contentDescription = description
|
||||
}
|
||||
},
|
||||
progress = { progress },
|
||||
strokeWidth = strokeWidth
|
||||
Crossfade(targetState = state, label = "State CrossFade") { state ->
|
||||
when (state) {
|
||||
State.COMPLETED -> Icon(
|
||||
Icons.Filled.CheckCircle,
|
||||
contentDescription = stringResource(R.string.step_completed),
|
||||
tint = Color(0xFF59B463),
|
||||
modifier = Modifier.size(size)
|
||||
)
|
||||
|
||||
State.FAILED -> Icon(
|
||||
Icons.Filled.Cancel,
|
||||
contentDescription = stringResource(R.string.step_failed),
|
||||
tint = MaterialTheme.colorScheme.error,
|
||||
modifier = Modifier.size(size)
|
||||
)
|
||||
|
||||
State.WAITING -> Icon(
|
||||
Icons.Outlined.Circle,
|
||||
contentDescription = stringResource(R.string.step_waiting),
|
||||
tint = MaterialTheme.colorScheme.onSurface.copy(.2f),
|
||||
modifier = Modifier.size(size)
|
||||
)
|
||||
|
||||
State.RUNNING -> {
|
||||
LoadingIndicator(
|
||||
modifier = stringResource(R.string.step_running).let { description ->
|
||||
Modifier
|
||||
.size(size)
|
||||
.semantics {
|
||||
contentDescription = description
|
||||
}
|
||||
},
|
||||
|
||||
progress = { progress },
|
||||
strokeWidth = strokeWidth
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -213,6 +213,12 @@ fun PatcherScreen(
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
) {
|
||||
var expandedCategory by rememberSaveable { mutableStateOf<StepCategory?>(null) }
|
||||
|
||||
val expandCategory: (StepCategory?) -> Unit = { category ->
|
||||
expandedCategory = category
|
||||
}
|
||||
|
||||
LinearProgressIndicator(
|
||||
progress = { viewModel.progress },
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
@@ -231,7 +237,12 @@ fun PatcherScreen(
|
||||
category = category,
|
||||
steps = steps,
|
||||
stepCount = if (category == StepCategory.PATCHING) viewModel.patchesProgress else null,
|
||||
stepProgressProvider = viewModel
|
||||
stepProgressProvider = viewModel,
|
||||
isExpanded = expandedCategory == category,
|
||||
onExpand = { expandCategory(category) },
|
||||
onClick = {
|
||||
expandCategory(if (expandedCategory == category) null else category)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,8 @@ package app.revanced.manager.ui.screen.settings
|
||||
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
@@ -10,10 +12,13 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.outlined.Delete
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.OutlinedCard
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TextButton
|
||||
@@ -28,6 +33,7 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
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.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -36,10 +42,10 @@ import app.revanced.manager.R
|
||||
import app.revanced.manager.network.downloader.DownloaderPluginState
|
||||
import app.revanced.manager.ui.component.AppLabel
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.ConfirmDialog
|
||||
import app.revanced.manager.ui.component.ExceptionViewerDialog
|
||||
import app.revanced.manager.ui.component.GroupHeader
|
||||
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
|
||||
import app.revanced.manager.ui.component.ConfirmDialog
|
||||
import app.revanced.manager.ui.component.haptics.HapticCheckbox
|
||||
import app.revanced.manager.ui.component.settings.SettingsListItem
|
||||
import app.revanced.manager.ui.viewmodel.DownloadsViewModel
|
||||
@@ -52,6 +58,7 @@ fun DownloadsSettingsScreen(
|
||||
onBackClick: () -> Unit,
|
||||
viewModel: DownloadsViewModel = koinViewModel()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val downloadedApps by viewModel.downloadedApps.collectAsStateWithLifecycle(emptyList())
|
||||
val pluginStates by viewModel.downloaderPluginStates.collectAsStateWithLifecycle()
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
|
||||
@@ -75,7 +82,7 @@ fun DownloadsSettingsScreen(
|
||||
onBackClick = onBackClick,
|
||||
actions = {
|
||||
if (viewModel.appSelection.isNotEmpty()) {
|
||||
IconButton(onClick = { showDeleteConfirmationDialog = true }) {
|
||||
IconButton(onClick = { viewModel.deleteApps() }) {
|
||||
Icon(Icons.Default.Delete, stringResource(R.string.delete))
|
||||
}
|
||||
}
|
||||
@@ -121,6 +128,11 @@ fun DownloadsSettingsScreen(
|
||||
.digest(androidSignature.toByteArray())
|
||||
hash.toHexString(format = HexFormat.UpperCase)
|
||||
}
|
||||
val appName = remember {
|
||||
packageInfo.applicationInfo?.loadLabel(context.packageManager)
|
||||
?.toString()
|
||||
?: packageName
|
||||
}
|
||||
|
||||
when (state) {
|
||||
is DownloaderPluginState.Loaded -> TrustDialog(
|
||||
@@ -130,6 +142,8 @@ fun DownloadsSettingsScreen(
|
||||
packageName,
|
||||
signature
|
||||
),
|
||||
pluginName = appName,
|
||||
signature = signature,
|
||||
onDismiss = ::dismiss,
|
||||
onConfirm = {
|
||||
viewModel.revokePluginTrust(packageName)
|
||||
@@ -147,10 +161,10 @@ fun DownloadsSettingsScreen(
|
||||
is DownloaderPluginState.Untrusted -> TrustDialog(
|
||||
title = R.string.downloader_plugin_trust_dialog_title,
|
||||
body = stringResource(
|
||||
R.string.downloader_plugin_trust_dialog_body,
|
||||
packageName,
|
||||
signature
|
||||
R.string.downloader_plugin_trust_dialog_body
|
||||
),
|
||||
pluginName = appName,
|
||||
signature = signature,
|
||||
onDismiss = ::dismiss,
|
||||
onConfirm = {
|
||||
viewModel.trustPlugin(packageName)
|
||||
@@ -226,6 +240,8 @@ fun DownloadsSettingsScreen(
|
||||
private fun TrustDialog(
|
||||
@StringRes title: Int,
|
||||
body: String,
|
||||
pluginName: String,
|
||||
signature: String,
|
||||
onDismiss: () -> Unit,
|
||||
onConfirm: () -> Unit
|
||||
) {
|
||||
@@ -238,10 +254,39 @@ private fun TrustDialog(
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.dismiss))
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
title = { Text(stringResource(title)) },
|
||||
text = { Text(body) }
|
||||
text = {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Text(body)
|
||||
Card {
|
||||
Column(
|
||||
Modifier.padding(12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.downloader_plugin_trust_dialog_plugin,
|
||||
pluginName
|
||||
),
|
||||
)
|
||||
OutlinedCard(
|
||||
colors = CardDefaults.outlinedCardColors(
|
||||
containerColor = MaterialTheme.colorScheme.surfaceContainerHighest
|
||||
)
|
||||
) {
|
||||
Text(
|
||||
stringResource(
|
||||
R.string.downloader_plugin_trust_dialog_signature,
|
||||
signature.chunked(2).joinToString(" ")
|
||||
), modifier = Modifier.padding(12.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package app.revanced.manager.ui.screen.settings
|
||||
|
||||
import android.os.Build
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
@@ -96,6 +97,14 @@ fun GeneralSettingsScreen(
|
||||
description = R.string.dynamic_color_description
|
||||
)
|
||||
}
|
||||
AnimatedVisibility(theme != Theme.LIGHT) {
|
||||
BooleanItem(
|
||||
preference = prefs.pureBlackTheme,
|
||||
coroutineScope = coroutineScope,
|
||||
headline = R.string.pure_black_theme,
|
||||
description = R.string.pure_black_theme_description
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ private val LightColorScheme = lightColorScheme(
|
||||
fun ReVancedManagerTheme(
|
||||
darkTheme: Boolean,
|
||||
dynamicColor: Boolean,
|
||||
pureBlackTheme: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
@@ -93,6 +94,10 @@ fun ReVancedManagerTheme(
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}.let {
|
||||
if (darkTheme && pureBlackTheme)
|
||||
it.copy(background = Color.Black, surface = Color.Black)
|
||||
else it
|
||||
}
|
||||
|
||||
val view = LocalView.current
|
||||
|
||||
@@ -377,22 +377,22 @@ class PatcherViewModel(
|
||||
try {
|
||||
isInstalling = true
|
||||
|
||||
val currentPackageInfo = pm.getPackageInfo(outputFile)
|
||||
?: throw Exception("Failed to load application info")
|
||||
|
||||
// If the app is currently installed
|
||||
val existingPackageInfo = pm.getPackageInfo(currentPackageInfo.packageName)
|
||||
if (existingPackageInfo != null) {
|
||||
// Check if the app version is less than the installed version
|
||||
if (pm.getVersionCode(currentPackageInfo) < pm.getVersionCode(existingPackageInfo)) {
|
||||
// Exit if the selected app version is less than the installed version
|
||||
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_CONFLICT
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
when (installType) {
|
||||
InstallType.DEFAULT -> {
|
||||
val currentPackageInfo = pm.getPackageInfo(outputFile)
|
||||
?: throw Exception("Failed to load application info")
|
||||
|
||||
// If the app is currently installed
|
||||
val existingPackageInfo = pm.getPackageInfo(currentPackageInfo.packageName)
|
||||
if (existingPackageInfo != null) {
|
||||
// Check if the app version is less than the installed version
|
||||
if (pm.getVersionCode(currentPackageInfo) < pm.getVersionCode(existingPackageInfo)) {
|
||||
// Exit if the selected app version is less than the installed version
|
||||
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_CONFLICT
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the app is mounted as root
|
||||
// If it is, unmount it first, silently
|
||||
if (rootInstaller.hasRootAccess() && rootInstaller.isAppMounted(packageName)) {
|
||||
@@ -412,16 +412,6 @@ class PatcherViewModel(
|
||||
packageInfo.label()
|
||||
}
|
||||
|
||||
// Check for base APK, first check if the app is already installed
|
||||
if (existingPackageInfo == null) {
|
||||
// If the app is not installed, check if the output file is a base apk
|
||||
if (currentPackageInfo.splitNames.isNotEmpty()) {
|
||||
// Exit if there is no base APK package
|
||||
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_INVALID
|
||||
return@launch
|
||||
}
|
||||
}
|
||||
|
||||
val inputVersion = input.selectedApp.version
|
||||
?: inputFile?.let(pm::getPackageInfo)?.versionName
|
||||
?: throw Exception("Failed to determine input APK version")
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<item quantity="other">%d patches</item>
|
||||
</plurals>
|
||||
<plurals name="patches_executed">
|
||||
<item quantity="one">Executed %d patch</item>
|
||||
<item quantity="other">Executed %d patches</item>
|
||||
<item quantity="one">Execute %d patch</item>
|
||||
<item quantity="other">Execute %d patches</item>
|
||||
</plurals>
|
||||
<plurals name="selected_count">
|
||||
<item quantity="other">%d selected</item>
|
||||
|
||||
@@ -1,3 +1,17 @@
|
||||
<!--
|
||||
Strings with new lines must be raw strings, where the string is wrapped in double quotes and new lines are regular line breaks and not \n
|
||||
Raw strings still require escaping embedded double quotes, but single quote characters can be escaped or used as-is.
|
||||
|
||||
Raw strings are required because Crowdin AI translations regularly gets confused and
|
||||
replace \n with an encoded new line character.
|
||||
|
||||
Bad:
|
||||
<string name="summary_key">First \'item\' text\nSecond \"item\" text</string>
|
||||
Good:
|
||||
<string name="summary_key">"First 'item' text
|
||||
Second \"item\" text"</string>
|
||||
|
||||
-->
|
||||
<resources>
|
||||
<string name="app_name">ReVanced Manager</string>
|
||||
<string name="patcher">Patcher</string>
|
||||
@@ -55,7 +69,7 @@
|
||||
<string name="network_metered_warning">You are currently on a metered connection. Data charges from your service provider may apply.</string>
|
||||
|
||||
<string name="apk_source_selector_item">Select APK source</string>
|
||||
<string name="apk_source_auto">Using all APK downloader</string>
|
||||
<string name="apk_source_auto">Using all APK downloaders</string>
|
||||
<string name="apk_source_downloader">Using %s</string>
|
||||
<string name="apk_source_installed">Using installed APK</string>
|
||||
<string name="apk_source_local">Using a local APK file</string>
|
||||
@@ -86,21 +100,31 @@
|
||||
<string name="contributors_description">View the contributors of ReVanced</string>
|
||||
<string name="dynamic_color">Dynamic color</string>
|
||||
<string name="dynamic_color_description">Adapt colors to the wallpaper</string>
|
||||
<string name="pure_black_theme">Pure black theme</string>
|
||||
<string name="pure_black_theme_description">Use pure black backgrounds for dark theme</string>
|
||||
<string name="theme">Theme</string>
|
||||
<string name="theme_description">Choose between light or dark theme</string>
|
||||
<string name="safeguards">Safeguards</string>
|
||||
<string name="patch_compat_check">Disable version compatibility check</string>
|
||||
<string name="patch_compat_check_description">The check restricts patches to compatible app versions</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="patch_compat_check_description">Do not restrict patches to compatible app versions</string>
|
||||
<string name="patch_compat_check_confirmation">"Selecting incompatible patches can result in a broken app.
|
||||
|
||||
Do you want to proceed 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="suggested_version_safeguard_confirmation">"Selecting an app that is not the suggested version may cause unexpected issues.
|
||||
|
||||
Do you want to proceed anyways?"</string>
|
||||
<string name="patch_selection_safeguard">Allow changing patch selection and options</string>
|
||||
<string name="patch_selection_safeguard_description">Do not prevent selecting or deselecting patches and customization of options</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.
|
||||
|
||||
Enable anyways?"</string>
|
||||
<string name="universal_patches_safeguard">Allow using universal patches</string>
|
||||
<string name="universal_patches_safeguard_description">Do not prevent using universal patches</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 patches are not as well tested as those that target specific apps.
|
||||
|
||||
Enable anyways?"</string>
|
||||
<string name="import_keystore">Import keystore</string>
|
||||
<string name="import_keystore_description">Import a custom keystore</string>
|
||||
<string name="import_keystore_dialog_title">Enter keystore credentials</string>
|
||||
@@ -116,7 +140,9 @@
|
||||
<string name="export_keystore_success">Exported keystore</string>
|
||||
<string name="regenerate_keystore">Regenerate keystore</string>
|
||||
<string name="regenerate_keystore_description">Generate a new keystore</string>
|
||||
<string name="regenerate_keystore_dialog_description">You are about to regenerate your keystore the manager will use during the patching process.\n\nYou will not be able to update the previously installed apps from this source.</string>
|
||||
<string name="regenerate_keystore_dialog_description">"You are about to regenerate your keystore the manager will use during the patching process.
|
||||
|
||||
You will not be able to update the previously installed apps from this source."</string>
|
||||
<string name="regenerate_keystore_success">The keystore has been successfully replaced</string>
|
||||
<string name="import_patch_selection">Import patch selection</string>
|
||||
<string name="import_patch_selection_description">Import patch selection from a JSON file</string>
|
||||
@@ -132,8 +158,8 @@
|
||||
<string name="reset_patch_options_description">Reset the stored patch options</string>
|
||||
<string name="reset_patch_selection_success">Patch selection has been reset</string>
|
||||
<string name="patch_selection_reset_all">Reset patch selection globally</string>
|
||||
<string name="patch_selection_reset_all_dialog_description">You are about to reset all the patch selections. You will need to manually select each patch again.</string>
|
||||
<string name="patch_selection_reset_all_description">Resets all the patch selections</string>
|
||||
<string name="patch_selection_reset_all_dialog_description">You are about to reset all patch selections. You will need to manually select each patch again.</string>
|
||||
<string name="patch_selection_reset_all_description">Resets all patch selections</string>
|
||||
<string name="patch_selection_reset_package">Reset patch selection for app</string>
|
||||
<string name="patch_selection_reset_package_dialog_description">You are about to reset the patch selection for the app \"%s\". You will have to manually select each patch again.</string>
|
||||
<string name="patch_selection_reset_package_description">Resets patch selection for a single app</string>
|
||||
@@ -147,7 +173,7 @@
|
||||
<string name="patch_options_reset_patches_dialog_description">You are about to reset the patch options for \"%s\". You will have to reapply each option again.</string>
|
||||
<string name="patch_options_reset_patches_description">Resets the patch options for a specific collection of patches</string>
|
||||
<string name="patch_options_reset_all">Reset patch options globally</string>
|
||||
<string name="patch_options_reset_all_dialog_description">You are about to reset patch options. You will have to reapply each option again.</string>
|
||||
<string name="patch_options_reset_all_dialog_description">You are about to reset all patch options. You will have to reapply each option again.</string>
|
||||
<string name="patch_options_reset_all_description">Resets all patch options</string>
|
||||
<string name="downloader_plugins">Plugins</string>
|
||||
<string name="downloader_plugin_state_trusted">Trusted</string>
|
||||
@@ -155,10 +181,12 @@
|
||||
<string name="downloader_plugin_state_untrusted">Untrusted</string>
|
||||
<string name="downloader_plugin_trust_dialog_title">Trust plugin?</string>
|
||||
<string name="downloader_plugin_revoke_trust_dialog_title">Revoke trust?</string>
|
||||
<string name="downloader_plugin_trust_dialog_body">Package name: %1$s\nSignature (SHA-256): %2$s</string>
|
||||
<string name="downloader_plugin_trust_dialog_body">Continuing will allow this plugin to run on your system.\n\nOnly enable this plugin if you trust it. Plugins can execute arbitrary code and may compromise your device.</string>
|
||||
<string name="downloader_plugin_trust_dialog_signature">Signature:\n\n%s</string>
|
||||
<string name="downloader_plugin_trust_dialog_plugin">Plugin:\n%s</string>
|
||||
<string name="downloader_plugin_delete_apps_title">Delete selected apps</string>
|
||||
<string name="downloader_plugin_delete_apps_description">Are you sure you want to delete the selected apps?</string>
|
||||
<string name="downloader_settings_no_apps">No downloaded apps found</string>
|
||||
<string name="downloader_settings_no_apps">No downloaded apps found.</string>
|
||||
|
||||
<string name="search_apps">Search apps…</string>
|
||||
<string name="loading_body">Loading…</string>
|
||||
@@ -191,7 +219,7 @@
|
||||
<string name="appearance">Appearance</string>
|
||||
<string name="downloaded_apps">Downloaded apps</string>
|
||||
<string name="process_runtime">Run Patcher in another process (experimental)</string>
|
||||
<string name="process_runtime_description">This is faster and allows Patcher to use more memory.</string>
|
||||
<string name="process_runtime_description">This is 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_description">The max amount of memory that the Patcher process can use (in megabytes)</string>
|
||||
<string name="debug_logs_export">Export debug logs</string>
|
||||
@@ -199,7 +227,7 @@
|
||||
<string name="debug_logs_export_failed">Failed to export logs</string>
|
||||
<string name="debug_logs_export_success">Exported logs</string>
|
||||
<string name="api_url">API URL</string>
|
||||
<string name="api_url_description">The API used to download necessary files.</string>
|
||||
<string name="api_url_description">The API used to download necessary files</string>
|
||||
<string name="api_url_dialog_title">Change API URL</string>
|
||||
<string name="api_url_dialog_description">Change the API URL of ReVanced Manager. ReVanced Manager uses the API to download patches and updates.</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>
|
||||
@@ -235,14 +263,23 @@
|
||||
<string name="patch_selection_reset_toast">Patch selection and options has been reset to recommended defaults</string>
|
||||
<string name="patch_options_reset_toast">Patch options have been reset</string>
|
||||
<string name="non_suggested_version_warning_title">Non suggested version</string>
|
||||
<string name="non_suggested_version_warning_description">The version of the app you have selected does not match the suggested version.\nPlease use the suggested version: %s\n\nTo continue anyway, disable \"Require suggested app version\" in the advanced settings.</string>
|
||||
<string name="non_suggested_version_warning_description">"The version of the app you have selected does not match the suggested version.
|
||||
Please use the suggested version: %s
|
||||
|
||||
To continue anyway, disable \"Require suggested app version\" in the advanced settings."</string>
|
||||
<string name="selection_warning_title">Stop using defaults?</string>
|
||||
<string name="selection_warning_description">It is recommended to use the default patch selection and options. Changing them may result in unexpected issues.\n\nYou need to turn on \"Allow changing patch selection\" in the advanced settings before toggling patches.</string>
|
||||
<string name="universal_patch_warning_description">Universal patches have a more generalized use and do not work as reliably as patches that target specific apps. You may encounter issues while using them.\n\nYou need to turn on \"Allow using universal patches\" in the advanced settings before using universal patches.</string>
|
||||
<string name="selection_warning_description">"It is recommended to use the default patch selection and options. Changing them may result in unexpected issues.
|
||||
|
||||
You need to turn on \"Allow changing patch selection and options\" in the advanced settings before toggling patches."</string>
|
||||
<string name="universal_patch_warning_description">"Universal patches have a more generalized use and do not work as reliably as patches that target specific apps. You may encounter issues while using them.
|
||||
|
||||
You need to turn on \"Allow using universal patches\" in the advanced settings before using universal patches."</string>
|
||||
<string name="this_version">This version</string>
|
||||
<string name="universal">Any app</string>
|
||||
<string name="search_patches">Search patches</string>
|
||||
<string name="app_version_not_compatible">This patch is not compatible with the selected app version (%1$s).\n\nIt is only compatible with the following version(s): %2$s.</string>
|
||||
<string name="app_version_not_compatible">"This patch is not compatible with the selected app version (%1$s)
|
||||
|
||||
It is only compatible with the following version(s): %2$s"</string>
|
||||
<string name="continue_with_version">Continue with this version?</string>
|
||||
<string name="version_not_compatible">Not all patches are compatible with this version (%s). Do you want to continue anyway?</string>
|
||||
<string name="download_application">Download application?</string>
|
||||
@@ -276,7 +313,7 @@
|
||||
<string name="downloader_app_not_found">Downloader did not find the app</string>
|
||||
<string name="downloader_error">Downloader error: %s</string>
|
||||
<string name="downloader_no_plugins_installed">No downloader installed.</string>
|
||||
<string name="downloader_no_plugins_available">There are downloader installed but none is trusted. Check your settings.</string>
|
||||
<string name="downloader_no_plugins_available">There are downloaders installed but none are trusted. Check your settings.</string>
|
||||
<string name="already_patched">Already patched</string>
|
||||
|
||||
<string name="patch_selector_sheet_filter_title">Filter</string>
|
||||
@@ -384,7 +421,8 @@
|
||||
<string name="save_with_count">Save (%1$s)</string>
|
||||
<string name="update">Update</string>
|
||||
<string name="empty">Empty</string>
|
||||
<string name="installing_message">Tap on <b>Update</b> when prompted.\nReVanced Manager will close when updating.</string>
|
||||
<string name="installing_message">"Tap on <b>Update</b> when prompted.
|
||||
ReVanced Manager will close when updating."</string>
|
||||
<string name="no_changelogs_found">No changelogs found</string>
|
||||
<string name="just_now">Just now</string>
|
||||
<string name="minutes_ago">%sm ago</string>
|
||||
@@ -403,7 +441,9 @@
|
||||
<string name="update_available_dialog_description">A new version of ReVanced Manager (%s) is available.</string>
|
||||
<string name="failed_to_download_update">Failed to download update: %s</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="download_confirmation_metered">You are currently on a metered connection, and data charges from your service provider may apply.\n\nDo you still want to continue?</string>
|
||||
<string name="download_confirmation_metered">"You are currently on a metered connection, and data charges from your service provider may apply.
|
||||
|
||||
Do you still want to continue?"</string>
|
||||
<string name="download_update_confirmation">Download update?</string>
|
||||
<string name="no_contributors_found">No contributors found</string>
|
||||
<string name="select">Select</string>
|
||||
@@ -441,12 +481,14 @@
|
||||
<string name="patches_prereleases">Use pre-releases</string>
|
||||
<string name="patches_prereleases_description">Use pre-release versions of %s</string>
|
||||
<string name="patches_url">Patches URL</string>
|
||||
<string name="incompatible_patches_dialog">These patches are not compatible with the selected app version (%1$s).\n\nClick on the patches to see more details.</string>
|
||||
<string name="incompatible_patches_dialog">"These patches are not compatible with the selected app version (%1$s).
|
||||
|
||||
Click on the patches to see more details."</string>
|
||||
<string name="incompatible_patch">Incompatible patch</string>
|
||||
<string name="any_version">Any</string>
|
||||
<string name="never_show_again">Never show again</string>
|
||||
<string name="show_manager_update_dialog_on_launch">Show update message on launch</string>
|
||||
<string name="show_manager_update_dialog_on_launch_description">Shows a popup notification whenever there is a new update available on launch.</string>
|
||||
<string name="show_manager_update_dialog_on_launch_description">Show a popup notification whenever a new update is available on launch</string>
|
||||
<string name="failed_to_import_keystore">Failed to import keystore</string>
|
||||
<string name="export">Export</string>
|
||||
<string name="confirm">Confirm</string>
|
||||
|
||||
@@ -13,9 +13,9 @@ Learn how to use ReVanced Manager to patch apps.
|
||||
7. Tap on the `Install` button to install the patched app[^4]
|
||||
|
||||
[^1]: Here you can see all the apps that are supported by ReVanced.
|
||||
You can also add custom apps by tapping on the `+` button in the top right corner.
|
||||
You can also add custom apps by tapping on the `Select from storage` button at the top.
|
||||
[^2]: It is recommended to use the default set of patches by tapping on the `Reset` button in the bottom right corner.
|
||||
[^3]: By default, all available downloader will be used to download the app.
|
||||
[^3]: By default, all available downloaders will be used to download the app.
|
||||
If you want to use a specific downloader, you can change it here.
|
||||
[^4]: You can export the patched app or the patch logs in the bottom left corner.
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
Learn how to manage downloaders.
|
||||
Refer to the [template](https://github.com/ReVanced/revanced-manager-downloader-template) if you are developer who wants to create a plugin.
|
||||
|
||||
Downloaders are Apk files and are installed, updated and uninstalled just like regular Android apps.
|
||||
Downloaders are APK files and are installed, updated and uninstalled just like regular Android apps.
|
||||
Downloaders can execute arbitrary code inside ReVanced Manager and must be marked as trusted before use. Manager will show a notification in the dashboard when a new downloader is discovered.
|
||||
Trust can also be granted and revoked under `Settings` > `Downloads`.
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ Learn how to update ReVanced Manager.
|
||||
3. Configure the update settings accordingly[^1]
|
||||
|
||||
[^1]: By default, ReVanced Manager will check for updates automatically on launch
|
||||
and let you when an update is available.
|
||||
and let you know when an update is available.
|
||||
|
||||
## ⏭️ What's next
|
||||
|
||||
|
||||
@@ -4,29 +4,29 @@ Learn how to configure ReVanced Manager.
|
||||
|
||||
## 🔧 Settings
|
||||
|
||||
- **Import & export**: Import or export patch selections, patch options and the signing keystore
|
||||
- **Downloads**: Enable or disable ReVanced Manager downloader and manage past downloaded apps here
|
||||
- **Import & export**: Import or export patch selections, patch options and the signing keystore
|
||||
- **Advanced**:
|
||||
- **API URL**: Set the URL of the ReVanced API, ReVanced Manager will use
|
||||
- **Disable version compatibility check**: Patching versions of apps the patches are explicitly compatible with is enforced.
|
||||
Disabling this will allow patching versions of apps the patches are not explicitly compatible with
|
||||
> ⚠️ Warning
|
||||
> Patches may fail patching versions they are not explicitly compatible with.
|
||||
> Unless you know what you are doing, it is recommended to keep this enabled.
|
||||
- **Allow changing patch selection**: The default selection of patches is enforced.
|
||||
Enabling this will allow you to change the patch selection
|
||||
> ⚠️ Warning
|
||||
> Changing the selection may cause unexpected issues.
|
||||
> Patches may fail on app versions they are not explicitly compatible with.
|
||||
> Unless you know what you are doing, it is recommended to keep this disabled.
|
||||
- **Require suggested app version**: Specific versions of apps is enforced based on the patch selection automatically.
|
||||
Disabling this will allow you to patch any version of apps
|
||||
> ⚠️ Warning
|
||||
> Patches not compatible with the selected version of the app will not be used.
|
||||
> Unless you know what you are doing, it is recommended to keep this enabled.
|
||||
- **Allow universal patches**: Patches that do not specify compatibility with an app explicitly are forcibly disabled.
|
||||
- **Allow changing patch selection and options**: The default selection of patches is enforced.
|
||||
Enabling this will allow you to change the patch selection
|
||||
> ⚠️ Warning
|
||||
> Changing the selection may cause unexpected issues.
|
||||
> Unless you know what you are doing, it is recommended to keep this disabled.
|
||||
- **Allow using universal patches**: Patches that do not specify compatibility with an app are forcibly disabled.
|
||||
Enabling this will allow selecting such patches
|
||||
> ⚠️ Warning
|
||||
> Universal patches do not specify compatibility with an app explicitly may not work on all apps regardless.
|
||||
> Universal patches do not specify compatibility with an app and may not work on all apps regardless.
|
||||
> Unless you know what you are doing, it is recommended to keep this disabled.
|
||||
- **About**: View more information and links about ReVanced and ReVanced Manager.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user