Compare commits

..

17 Commits

Author SHA1 Message Date
semantic-release-bot
e5097b5ecd chore: Release v1.26.0-dev.9 [skip ci]
# app [1.26.0-dev.9](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.8...v1.26.0-dev.9) (2025-10-06)

### Bug Fixes

* Instantly re-fetch patch bundle on pre-release preference update ([d5671db](d5671db3a7)), closes [#2784](https://github.com/ReVanced/revanced-manager/issues/2784)
2025-10-06 00:44:24 +00:00
Ushie
d5671db3a7 fix: Instantly re-fetch patch bundle on pre-release preference update
Closes #2784
2025-10-06 03:36:14 +03:00
semantic-release-bot
a314ba209d chore: Release v1.26.0-dev.8 [skip ci]
# app [1.26.0-dev.8](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.7...v1.26.0-dev.8) (2025-10-06)

### Bug Fixes

* Offcenter loading indicator in AppSelector ([12d92ba](12d92ba811))
2025-10-06 00:18:33 +00:00
Ushie
12d92ba811 fix: Offcenter loading indicator in AppSelector 2025-10-06 03:10:16 +03:00
semantic-release-bot
ffd08c737c chore: Release v1.26.0-dev.7 [skip ci]
# app [1.26.0-dev.7](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.6...v1.26.0-dev.7) (2025-10-03)

### Bug Fixes

* Improve consistency between pre-release toggles ([e1b768c](e1b768c467))
2025-10-03 16:34:21 +00:00
Ushie
e1b768c467 fix: Improve consistency between pre-release toggles 2025-10-03 19:26:31 +03:00
semantic-release-bot
df6ecd27dd chore: Release v1.26.0-dev.6 [skip ci]
# app [1.26.0-dev.6](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.5...v1.26.0-dev.6) (2025-10-03)

### Bug Fixes

* Broken version comparison ([c327857](c327857823))

### Features

* Open contributor's GitHub profile when clicked ([#2775](https://github.com/ReVanced/revanced-manager/issues/2775)) ([2571cb8](2571cb8c11))
2025-10-03 15:55:06 +00:00
Ushie
2571cb8c11 feat: Open contributor's GitHub profile when clicked (#2775) 2025-10-03 18:47:32 +03:00
Ax333l
c327857823 fix: Broken version comparison 2025-10-03 17:44:07 +02:00
semantic-release-bot
03160ccef1 chore: Release v1.26.0-dev.5 [skip ci]
# app [1.26.0-dev.5](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.4...v1.26.0-dev.5) (2025-10-03)

### Features

* Toggle to use pre-release versions of ReVanced Patches ([08cec67](08cec674bb))
2025-10-03 15:37:24 +00:00
Ax333l
08cec674bb feat: Toggle to use pre-release versions of ReVanced Patches 2025-10-03 17:29:06 +02:00
semantic-release-bot
95fb97ec31 chore: Release v1.26.0-dev.4 [skip ci]
# app [1.26.0-dev.4](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.3...v1.26.0-dev.4) (2025-10-03)

### Bug Fixes

* add newlines to debug logs ([4753873](4753873866))
2025-10-03 14:34:30 +00:00
Ax333l
4753873866 fix: add newlines to debug logs 2025-10-03 16:26:54 +02:00
semantic-release-bot
ca322f2da8 chore: Release v1.26.0-dev.3 [skip ci]
# app [1.26.0-dev.3](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.2...v1.26.0-dev.3) (2025-10-03)

### Features

* Toggle to use pre-release versions of ReVanced Manager ([#2773](https://github.com/ReVanced/revanced-manager/issues/2773)) ([d758964](d758964742))
2025-10-03 14:26:09 +00:00
Ushie
d758964742 feat: Toggle to use pre-release versions of ReVanced Manager (#2773) 2025-10-03 17:18:27 +03:00
semantic-release-bot
a592e0b302 chore: Release v1.26.0-dev.2 [skip ci]
# app [1.26.0-dev.2](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.1...v1.26.0-dev.2) (2025-10-03)

### Bug Fixes

* Migration of keystore, by fixing mislabeling of alias as cn ([#2769](https://github.com/ReVanced/revanced-manager/issues/2769)) ([aeab639](aeab639b2b))
2025-10-03 13:38:38 +00:00
Ushie
aeab639b2b fix: Migration of keystore, by fixing mislabeling of alias as cn (#2769) 2025-10-03 16:31:05 +03:00
15 changed files with 162 additions and 31 deletions

View File

@@ -1,3 +1,64 @@
# app [1.26.0-dev.9](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.8...v1.26.0-dev.9) (2025-10-06)
### Bug Fixes
* Instantly re-fetch patch bundle on pre-release preference update ([d5671db](https://github.com/ReVanced/revanced-manager/commit/d5671db3a77541c07bbbb4c3baca02f3ba0703f2)), closes [#2784](https://github.com/ReVanced/revanced-manager/issues/2784)
# app [1.26.0-dev.8](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.7...v1.26.0-dev.8) (2025-10-06)
### Bug Fixes
* Offcenter loading indicator in AppSelector ([12d92ba](https://github.com/ReVanced/revanced-manager/commit/12d92ba8110f5d1ac78aeecfa575444b5c53f561))
# app [1.26.0-dev.7](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.6...v1.26.0-dev.7) (2025-10-03)
### Bug Fixes
* Improve consistency between pre-release toggles ([e1b768c](https://github.com/ReVanced/revanced-manager/commit/e1b768c4679ecae8bff8007bdab56ff6544b12b6))
# app [1.26.0-dev.6](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.5...v1.26.0-dev.6) (2025-10-03)
### Bug Fixes
* Broken version comparison ([c327857](https://github.com/ReVanced/revanced-manager/commit/c3278578237dcddd9e7ab79ee80a02fdeef9604d))
### Features
* Open contributor's GitHub profile when clicked ([#2775](https://github.com/ReVanced/revanced-manager/issues/2775)) ([2571cb8](https://github.com/ReVanced/revanced-manager/commit/2571cb8c1108e9c1ed84950f17692c09d66e0556))
# app [1.26.0-dev.5](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.4...v1.26.0-dev.5) (2025-10-03)
### Features
* Toggle to use pre-release versions of ReVanced Patches ([08cec67](https://github.com/ReVanced/revanced-manager/commit/08cec674bbbe5297090ac5ee6039569975fbe9e7))
# app [1.26.0-dev.4](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.3...v1.26.0-dev.4) (2025-10-03)
### Bug Fixes
* add newlines to debug logs ([4753873](https://github.com/ReVanced/revanced-manager/commit/4753873866b575e2dcb160020df63f63862c8f33))
# app [1.26.0-dev.3](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.2...v1.26.0-dev.3) (2025-10-03)
### Features
* Toggle to use pre-release versions of ReVanced Manager ([#2773](https://github.com/ReVanced/revanced-manager/issues/2773)) ([d758964](https://github.com/ReVanced/revanced-manager/commit/d7589647426b3d3438161a2f0b59bf4f154ac34b))
# app [1.26.0-dev.2](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.1...v1.26.0-dev.2) (2025-10-03)
### Bug Fixes
* Migration of keystore, by fixing mislabeling of alias as cn ([#2769](https://github.com/ReVanced/revanced-manager/issues/2769)) ([aeab639](https://github.com/ReVanced/revanced-manager/commit/aeab639b2b09e8bbd2478cfbf5a518586405c0f7))
# app [1.26.0-dev.1](https://github.com/ReVanced/revanced-manager/compare/v1.25.1...v1.26.0-dev.1) (2025-10-02)

View File

@@ -1 +1 @@
version = 1.26.0-dev.1
version = 1.26.0-dev.9

View File

@@ -27,25 +27,25 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
private val keystorePath =
app.getDir("signing", Context.MODE_PRIVATE).resolve("manager.keystore")
private suspend fun updatePrefs(cn: String, pass: String) = prefs.edit {
prefs.keystoreCommonName.value = cn
private suspend fun updatePrefs(alias: String, pass: String) = prefs.edit {
prefs.keystoreAlias.value = alias
prefs.keystorePass.value = pass
}
private suspend fun signingDetails(path: File = keystorePath) = ApkUtils.KeyStoreDetails(
keyStore = path,
keyStorePassword = null,
alias = prefs.keystoreCommonName.get(),
alias = prefs.keystoreAlias.get(),
password = prefs.keystorePass.get()
)
suspend fun sign(input: File, output: File) = withContext(Dispatchers.Default) {
ApkUtils.signApk(input, output, prefs.keystoreCommonName.get(), signingDetails())
ApkUtils.signApk(input, output, prefs.keystoreAlias.get(), signingDetails())
}
suspend fun regenerate() = withContext(Dispatchers.Default) {
val keyCertPair = ApkSigner.newPrivateKeyCertificatePair(
prefs.keystoreCommonName.get(),
prefs.keystoreAlias.get(),
eightYearsFromNow
)
val ks = ApkSigner.newKeyStore(
@@ -64,13 +64,13 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
updatePrefs(DEFAULT, DEFAULT)
}
suspend fun import(cn: String, pass: String, keystore: InputStream): Boolean {
suspend fun import(alias: String, pass: String, keystore: InputStream): Boolean {
val keystoreData = withContext(Dispatchers.IO) { keystore.readBytes() }
try {
val ks = ApkSigner.readKeyStore(ByteArrayInputStream(keystoreData), null)
ApkSigner.readPrivateKeyCertificatePair(ks, cn, pass)
ApkSigner.readPrivateKeyCertificatePair(ks, alias, pass)
} catch (_: UnrecoverableKeyException) {
return false
} catch (_: IllegalArgumentException) {
@@ -81,7 +81,7 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
Files.write(keystorePath.toPath(), keystoreData)
}
updatePrefs(cn, pass)
updatePrefs(alias, pass)
return true
}

View File

@@ -16,12 +16,14 @@ class PreferencesManager(
val useProcessRuntime = booleanPreference("use_process_runtime", false)
val patcherProcessMemoryLimit = intPreference("process_runtime_memory_limit", 700)
val keystoreCommonName = stringPreference("keystore_cn", KeystoreManager.DEFAULT)
val keystoreAlias = stringPreference("keystore_alias", KeystoreManager.DEFAULT)
val keystorePass = stringPreference("keystore_pass", KeystoreManager.DEFAULT)
val firstLaunch = booleanPreference("first_launch", true)
val managerAutoUpdates = booleanPreference("manager_auto_updates", false)
val showManagerUpdateDialogOnLaunch = booleanPreference("show_manager_update_dialog_on_launch", true)
val useManagerPrereleases = booleanPreference("manager_prereleases", false)
val usePatchesPrereleases = booleanPreference("patches_prereleases", false)
val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false)
val disableSelectionWarning = booleanPreference("disable_selection_warning", false)

View File

@@ -1,6 +1,6 @@
package app.revanced.manager.network.api
import android.os.Build
import app.revanced.manager.BuildConfig
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.network.dto.ReVancedAsset
import app.revanced.manager.network.dto.ReVancedGitRepository
@@ -30,11 +30,12 @@ class ReVancedAPI(
private suspend inline fun <reified T> request(route: String) = request<T>(apiUrl(), route)
suspend fun getAppUpdate() =
getLatestAppInfo().getOrThrow().takeIf { it.version != Build.VERSION.RELEASE }
getLatestAppInfo().getOrThrow().takeIf { it.version.removePrefix("v") != BuildConfig.VERSION_NAME }
suspend fun getLatestAppInfo() = request<ReVancedAsset>("manager")
suspend fun getLatestAppInfo() =
request<ReVancedAsset>("manager?prerelease=${prefs.useManagerPrereleases.get()}")
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches")
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches?prerelease=${prefs.usePatchesPrereleases.get()}")
suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors")

View File

@@ -45,6 +45,7 @@ import app.revanced.manager.domain.bundles.LocalPatchBundle
import app.revanced.manager.domain.bundles.PatchBundleSource
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.isDefault
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.domain.repository.PatchBundleRepository
import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.ExceptionViewerDialog
@@ -65,12 +66,14 @@ fun BundleInformationDialog(
) {
val bundleRepo = koinInject<PatchBundleRepository>()
val networkInfo = koinInject<NetworkInfo>()
val prefs = koinInject<PreferencesManager>()
val hasNetwork = remember { networkInfo.isConnected() }
val composableScope = rememberCoroutineScope()
var viewCurrentBundlePatches by remember { mutableStateOf(false) }
val isLocal = src is LocalPatchBundle
val bundleManifestAttributes = src.patchBundle?.manifestAttributes
val (autoUpdate, endpoint) = src.asRemoteOrNull?.let { it.autoUpdate to it.endpoint } ?: (null to null)
val (autoUpdate, endpoint) = src.asRemoteOrNull?.let { it.autoUpdate to it.endpoint }
?: (null to null)
fun onAutoUpdateChange(new: Boolean) = composableScope.launch {
with(bundleRepo) {
@@ -173,6 +176,34 @@ fun BundleInformationDialog(
)
}
if (src.isDefault) {
val useBundlePrerelease by prefs.usePatchesPrereleases.getAsState()
BundleListItem(
headlineText = stringResource(R.string.patches_prereleases),
supportingText = stringResource(R.string.patches_prereleases_description, src.name),
trailingContent = {
HapticSwitch(
checked = useBundlePrerelease,
onCheckedChange = {
composableScope.launch {
prefs.usePatchesPrereleases.update(
it
)
onUpdate()
}
}
)
},
modifier = Modifier.clickable {
composableScope.launch {
prefs.usePatchesPrereleases.update(!useBundlePrerelease)
onUpdate()
}
}
)
}
endpoint?.takeUnless { src.isDefault }?.let { url ->
var showUrlInputDialog by rememberSaveable {
mutableStateOf(false)

View File

@@ -234,7 +234,13 @@ fun AppSelectorScreen(
}
} else {
item { LoadingIndicator() }
item {
Box(
modifier = Modifier.fillParentMaxSize(), contentAlignment = Alignment.Center
) {
LoadingIndicator()
}
}
}
}
}

View File

@@ -1,6 +1,7 @@
package app.revanced.manager.ui.screen.settings
import androidx.compose.foundation.border
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxWithConstraints
@@ -34,6 +35,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.UriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
@@ -57,6 +60,7 @@ fun ContributorSettingsScreen(
) {
val repositories = viewModel.repositories
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(rememberTopAppBarState())
val uriHandler = LocalUriHandler.current
Scaffold(
topBar = {
@@ -93,7 +97,8 @@ fun ContributorSettingsScreen(
) {
ContributorsCard(
title = it.name,
contributors = it.contributors
contributors = it.contributors,
uriHandler = uriHandler
)
}
}
@@ -115,7 +120,8 @@ fun ContributorsCard(
title: String,
contributors: List<ReVancedContributor>,
itemsPerPage: Int = 12,
numberOfRows: Int = 2
numberOfRows: Int = 2,
uriHandler: UriHandler
) {
val itemsPerRow = (itemsPerPage / numberOfRows)
@@ -172,7 +178,11 @@ fun ContributorsCard(
contributorsByPage[page].forEach {
if (itemSize > 100.dp) {
Row(
modifier = Modifier.width(itemSize - 1.dp), // we delete 1.dp to account for not-so divisible numbers
modifier = Modifier
.width(itemSize - 1.dp)
.clickable {
uriHandler.openUri("https://github.com/${it.username}")
}, // we delete 1.dp to account for not-so divisible numbers
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
@@ -203,6 +213,9 @@ fun ContributorsCard(
modifier = Modifier
.size(size = (itemSize - 1.dp).coerceAtMost(50.dp)) // we delete 1.dp to account for not-so divisible numbers
.clip(CircleShape)
.clickable {
uriHandler.openUri("https://github.com/${it.username}")
}
)
}
}

View File

@@ -104,10 +104,10 @@ fun ImportExportSettingsScreen(
if (vm.showCredentialsDialog) {
KeystoreCredentialsDialog(
onDismissRequest = vm::cancelKeystoreImport,
onSubmit = { cn, pass ->
onSubmit = { alias, pass ->
vm.viewModelScope.launch {
uiSafe(context, R.string.failed_to_import_keystore, "Failed to import keystore") {
val result = vm.tryKeystoreImport(cn, pass)
val result = vm.tryKeystoreImport(alias, pass)
if (!result) context.toast(context.getString(R.string.import_keystore_wrong_credentials))
}
}
@@ -382,7 +382,7 @@ fun KeystoreCredentialsDialog(
onDismissRequest: () -> Unit,
onSubmit: (String, String) -> Unit
) {
var cn by rememberSaveable { mutableStateOf("") }
var alias by rememberSaveable { mutableStateOf("") }
var pass by rememberSaveable { mutableStateOf("") }
AlertDialog(
@@ -390,7 +390,7 @@ fun KeystoreCredentialsDialog(
confirmButton = {
TextButton(
onClick = {
onSubmit(cn, pass)
onSubmit(alias, pass)
}
) {
Text(stringResource(R.string.import_keystore_dialog_button))
@@ -422,8 +422,8 @@ fun KeystoreCredentialsDialog(
color = MaterialTheme.colorScheme.onSurfaceVariant
)
OutlinedTextField(
value = cn,
onValueChange = { cn = it },
value = alias,
onValueChange = { alias = it },
label = { Text(stringResource(R.string.import_keystore_dialog_alias_field)) }
)
PasswordField(

View File

@@ -92,6 +92,12 @@ fun UpdatesSettingsScreen(
headline = R.string.show_manager_update_dialog_on_launch,
description = R.string.show_manager_update_dialog_on_launch_description
)
BooleanItem(
preference = vm.useManagerPrereleases,
headline = R.string.manager_prereleases,
description = R.string.manager_prereleases_description
)
}
}
}

View File

@@ -47,7 +47,7 @@ class AdvancedSettingsViewModel(
app.contentResolver.openOutputStream(target)!!.bufferedWriter().use { writer ->
val consumer = Redirect.Consume { flow ->
flow.onEach {
writer.write(it)
writer.write("${it}\n")
}.flowOn(Dispatchers.IO).collect()
}

View File

@@ -154,12 +154,12 @@ class ImportExportViewModel(
keystoreImportPath = null
}
suspend fun tryKeystoreImport(cn: String, pass: String) =
tryKeystoreImport(cn, pass, keystoreImportPath!!)
suspend fun tryKeystoreImport(alias: String, pass: String) =
tryKeystoreImport(alias, pass, keystoreImportPath!!)
private suspend fun tryKeystoreImport(cn: String, pass: String, path: Path): Boolean {
private suspend fun tryKeystoreImport(alias: String, pass: String, path: Path): Boolean {
path.inputStream().use { stream ->
if (keystoreManager.import(cn, pass, stream)) {
if (keystoreManager.import(alias, pass, stream)) {
app.toast(app.getString(R.string.import_keystore_success))
cancelKeystoreImport()
return true

View File

@@ -120,6 +120,10 @@ class MainViewModel(
settings.useDynamicTheme?.let { dynamicColor ->
prefs.dynamicColor.update(dynamicColor)
}
settings.usePrereleases?.let { prereleases ->
prefs.useManagerPrereleases.update(prereleases)
prefs.usePatchesPrereleases.update(prereleases)
}
settings.apiUrl?.let { api ->
prefs.api.update(api.removeSuffix("/"))
}
@@ -143,7 +147,7 @@ class MainViewModel(
settings.keystore?.let { keystore ->
val keystoreBytes = Base64.decode(keystore, Base64.DEFAULT)
keystoreManager.import(
"ReVanced",
"alias",
settings.keystorePassword,
keystoreBytes.inputStream()
)
@@ -159,6 +163,7 @@ class MainViewModel(
val keystorePassword: String,
val themeMode: Int? = null,
val useDynamicTheme: Boolean? = null,
val usePrereleases: Boolean? = null,
val apiUrl: String? = null,
val experimentalPatchesEnabled: Boolean? = null,
val patchesAutoUpdate: Boolean? = null,

View File

@@ -17,6 +17,8 @@ class UpdatesSettingsViewModel(
) : ViewModel() {
val managerAutoUpdates = prefs.managerAutoUpdates
val showManagerUpdateDialogOnLaunch = prefs.showManagerUpdateDialogOnLaunch
val useManagerPrereleases = prefs.useManagerPrereleases
val isConnected: Boolean
get() = network.isConnected()

View File

@@ -368,6 +368,8 @@
<string name="manual_update_check_description">Manually check for updates</string>
<string name="update_checking_manager">Check for updates on launch</string>
<string name="update_checking_manager_description">Check for new versions of ReVanced Manager when the application starts</string>
<string name="manager_prereleases">Use pre-releases</string>
<string name="manager_prereleases_description">Use pre-release versions of ReVanced Manager</string>
<string name="changelog">View changelogs</string>
<string name="changelog_loading">Loading changelog</string>
<string name="changelog_download_fail">Failed to download changelog: %s</string>
@@ -435,6 +437,8 @@
<string name="auto_update">Auto update</string>
<string name="add_patches">Add patches</string>
<string name="auto_update_description">Automatically update when a new version is available</string>
<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_patch">Incompatible patch</string>