mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-19 17:23:58 +00:00
Compare commits
8 Commits
v1.26.0-de
...
v1.26.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03160ccef1 | ||
|
|
08cec674bb | ||
|
|
95fb97ec31 | ||
|
|
4753873866 | ||
|
|
ca322f2da8 | ||
|
|
d758964742 | ||
|
|
a592e0b302 | ||
|
|
aeab639b2b |
@@ -1,3 +1,31 @@
|
|||||||
|
# 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)
|
# app [1.26.0-dev.1](https://github.com/ReVanced/revanced-manager/compare/v1.25.1...v1.26.0-dev.1) (2025-10-02)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
version = 1.26.0-dev.1
|
version = 1.26.0-dev.5
|
||||||
|
|||||||
@@ -27,25 +27,25 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
|
|||||||
private val keystorePath =
|
private val keystorePath =
|
||||||
app.getDir("signing", Context.MODE_PRIVATE).resolve("manager.keystore")
|
app.getDir("signing", Context.MODE_PRIVATE).resolve("manager.keystore")
|
||||||
|
|
||||||
private suspend fun updatePrefs(cn: String, pass: String) = prefs.edit {
|
private suspend fun updatePrefs(alias: String, pass: String) = prefs.edit {
|
||||||
prefs.keystoreCommonName.value = cn
|
prefs.keystoreAlias.value = alias
|
||||||
prefs.keystorePass.value = pass
|
prefs.keystorePass.value = pass
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun signingDetails(path: File = keystorePath) = ApkUtils.KeyStoreDetails(
|
private suspend fun signingDetails(path: File = keystorePath) = ApkUtils.KeyStoreDetails(
|
||||||
keyStore = path,
|
keyStore = path,
|
||||||
keyStorePassword = null,
|
keyStorePassword = null,
|
||||||
alias = prefs.keystoreCommonName.get(),
|
alias = prefs.keystoreAlias.get(),
|
||||||
password = prefs.keystorePass.get()
|
password = prefs.keystorePass.get()
|
||||||
)
|
)
|
||||||
|
|
||||||
suspend fun sign(input: File, output: File) = withContext(Dispatchers.Default) {
|
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) {
|
suspend fun regenerate() = withContext(Dispatchers.Default) {
|
||||||
val keyCertPair = ApkSigner.newPrivateKeyCertificatePair(
|
val keyCertPair = ApkSigner.newPrivateKeyCertificatePair(
|
||||||
prefs.keystoreCommonName.get(),
|
prefs.keystoreAlias.get(),
|
||||||
eightYearsFromNow
|
eightYearsFromNow
|
||||||
)
|
)
|
||||||
val ks = ApkSigner.newKeyStore(
|
val ks = ApkSigner.newKeyStore(
|
||||||
@@ -64,13 +64,13 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
|
|||||||
updatePrefs(DEFAULT, DEFAULT)
|
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() }
|
val keystoreData = withContext(Dispatchers.IO) { keystore.readBytes() }
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val ks = ApkSigner.readKeyStore(ByteArrayInputStream(keystoreData), null)
|
val ks = ApkSigner.readKeyStore(ByteArrayInputStream(keystoreData), null)
|
||||||
|
|
||||||
ApkSigner.readPrivateKeyCertificatePair(ks, cn, pass)
|
ApkSigner.readPrivateKeyCertificatePair(ks, alias, pass)
|
||||||
} catch (_: UnrecoverableKeyException) {
|
} catch (_: UnrecoverableKeyException) {
|
||||||
return false
|
return false
|
||||||
} catch (_: IllegalArgumentException) {
|
} catch (_: IllegalArgumentException) {
|
||||||
@@ -81,7 +81,7 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
|
|||||||
Files.write(keystorePath.toPath(), keystoreData)
|
Files.write(keystorePath.toPath(), keystoreData)
|
||||||
}
|
}
|
||||||
|
|
||||||
updatePrefs(cn, pass)
|
updatePrefs(alias, pass)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,12 +16,14 @@ class PreferencesManager(
|
|||||||
val useProcessRuntime = booleanPreference("use_process_runtime", false)
|
val useProcessRuntime = booleanPreference("use_process_runtime", false)
|
||||||
val patcherProcessMemoryLimit = intPreference("process_runtime_memory_limit", 700)
|
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 keystorePass = stringPreference("keystore_pass", KeystoreManager.DEFAULT)
|
||||||
|
|
||||||
val firstLaunch = booleanPreference("first_launch", true)
|
val firstLaunch = booleanPreference("first_launch", true)
|
||||||
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 useManagerPrereleases = booleanPreference("manager_prereleases", false)
|
||||||
|
val usePatchesPrereleases = booleanPreference("patches_prereleases", false)
|
||||||
|
|
||||||
val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false)
|
val disablePatchVersionCompatCheck = booleanPreference("disable_patch_version_compatibility_check", false)
|
||||||
val disableSelectionWarning = booleanPreference("disable_selection_warning", false)
|
val disableSelectionWarning = booleanPreference("disable_selection_warning", false)
|
||||||
|
|||||||
@@ -32,9 +32,10 @@ class ReVancedAPI(
|
|||||||
suspend fun getAppUpdate() =
|
suspend fun getAppUpdate() =
|
||||||
getLatestAppInfo().getOrThrow().takeIf { it.version != Build.VERSION.RELEASE }
|
getLatestAppInfo().getOrThrow().takeIf { it.version != Build.VERSION.RELEASE }
|
||||||
|
|
||||||
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")
|
suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors")
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
||||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.isDefault
|
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.domain.repository.PatchBundleRepository
|
||||||
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
import app.revanced.manager.ui.component.ColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.ExceptionViewerDialog
|
import app.revanced.manager.ui.component.ExceptionViewerDialog
|
||||||
@@ -65,12 +66,14 @@ fun BundleInformationDialog(
|
|||||||
) {
|
) {
|
||||||
val bundleRepo = koinInject<PatchBundleRepository>()
|
val bundleRepo = koinInject<PatchBundleRepository>()
|
||||||
val networkInfo = koinInject<NetworkInfo>()
|
val networkInfo = koinInject<NetworkInfo>()
|
||||||
|
val prefs = koinInject<PreferencesManager>()
|
||||||
val hasNetwork = remember { networkInfo.isConnected() }
|
val hasNetwork = remember { networkInfo.isConnected() }
|
||||||
val composableScope = rememberCoroutineScope()
|
val composableScope = rememberCoroutineScope()
|
||||||
var viewCurrentBundlePatches by remember { mutableStateOf(false) }
|
var viewCurrentBundlePatches by remember { mutableStateOf(false) }
|
||||||
val isLocal = src is LocalPatchBundle
|
val isLocal = src is LocalPatchBundle
|
||||||
val bundleManifestAttributes = src.patchBundle?.manifestAttributes
|
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 {
|
fun onAutoUpdateChange(new: Boolean) = composableScope.launch {
|
||||||
with(bundleRepo) {
|
with(bundleRepo) {
|
||||||
@@ -173,6 +176,32 @@ fun BundleInformationDialog(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (src.isDefault) {
|
||||||
|
val usePrereleases by prefs.usePatchesPrereleases.getAsState()
|
||||||
|
|
||||||
|
BundleListItem(
|
||||||
|
headlineText = stringResource(R.string.patches_prereleases),
|
||||||
|
supportingText = stringResource(R.string.patches_prereleases_description),
|
||||||
|
trailingContent = {
|
||||||
|
HapticSwitch(
|
||||||
|
checked = usePrereleases,
|
||||||
|
onCheckedChange = {
|
||||||
|
composableScope.launch {
|
||||||
|
prefs.usePatchesPrereleases.update(
|
||||||
|
it
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
modifier = Modifier.clickable {
|
||||||
|
composableScope.launch {
|
||||||
|
prefs.usePatchesPrereleases.update(!usePrereleases)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
endpoint?.takeUnless { src.isDefault }?.let { url ->
|
endpoint?.takeUnless { src.isDefault }?.let { url ->
|
||||||
var showUrlInputDialog by rememberSaveable {
|
var showUrlInputDialog by rememberSaveable {
|
||||||
mutableStateOf(false)
|
mutableStateOf(false)
|
||||||
|
|||||||
@@ -104,10 +104,10 @@ fun ImportExportSettingsScreen(
|
|||||||
if (vm.showCredentialsDialog) {
|
if (vm.showCredentialsDialog) {
|
||||||
KeystoreCredentialsDialog(
|
KeystoreCredentialsDialog(
|
||||||
onDismissRequest = vm::cancelKeystoreImport,
|
onDismissRequest = vm::cancelKeystoreImport,
|
||||||
onSubmit = { cn, pass ->
|
onSubmit = { alias, pass ->
|
||||||
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(alias, pass)
|
||||||
if (!result) context.toast(context.getString(R.string.import_keystore_wrong_credentials))
|
if (!result) context.toast(context.getString(R.string.import_keystore_wrong_credentials))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -382,7 +382,7 @@ fun KeystoreCredentialsDialog(
|
|||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
onSubmit: (String, String) -> Unit
|
onSubmit: (String, String) -> Unit
|
||||||
) {
|
) {
|
||||||
var cn by rememberSaveable { mutableStateOf("") }
|
var alias by rememberSaveable { mutableStateOf("") }
|
||||||
var pass by rememberSaveable { mutableStateOf("") }
|
var pass by rememberSaveable { mutableStateOf("") }
|
||||||
|
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
@@ -390,7 +390,7 @@ fun KeystoreCredentialsDialog(
|
|||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
onSubmit(cn, pass)
|
onSubmit(alias, pass)
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Text(stringResource(R.string.import_keystore_dialog_button))
|
Text(stringResource(R.string.import_keystore_dialog_button))
|
||||||
@@ -422,8 +422,8 @@ fun KeystoreCredentialsDialog(
|
|||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
OutlinedTextField(
|
OutlinedTextField(
|
||||||
value = cn,
|
value = alias,
|
||||||
onValueChange = { cn = it },
|
onValueChange = { alias = it },
|
||||||
label = { Text(stringResource(R.string.import_keystore_dialog_alias_field)) }
|
label = { Text(stringResource(R.string.import_keystore_dialog_alias_field)) }
|
||||||
)
|
)
|
||||||
PasswordField(
|
PasswordField(
|
||||||
|
|||||||
@@ -92,6 +92,12 @@ fun UpdatesSettingsScreen(
|
|||||||
headline = R.string.show_manager_update_dialog_on_launch,
|
headline = R.string.show_manager_update_dialog_on_launch,
|
||||||
description = R.string.show_manager_update_dialog_on_launch_description
|
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
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ class AdvancedSettingsViewModel(
|
|||||||
app.contentResolver.openOutputStream(target)!!.bufferedWriter().use { writer ->
|
app.contentResolver.openOutputStream(target)!!.bufferedWriter().use { writer ->
|
||||||
val consumer = Redirect.Consume { flow ->
|
val consumer = Redirect.Consume { flow ->
|
||||||
flow.onEach {
|
flow.onEach {
|
||||||
writer.write(it)
|
writer.write("${it}\n")
|
||||||
}.flowOn(Dispatchers.IO).collect()
|
}.flowOn(Dispatchers.IO).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -154,12 +154,12 @@ class ImportExportViewModel(
|
|||||||
keystoreImportPath = null
|
keystoreImportPath = null
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun tryKeystoreImport(cn: String, pass: String) =
|
suspend fun tryKeystoreImport(alias: String, pass: String) =
|
||||||
tryKeystoreImport(cn, pass, keystoreImportPath!!)
|
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 ->
|
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))
|
app.toast(app.getString(R.string.import_keystore_success))
|
||||||
cancelKeystoreImport()
|
cancelKeystoreImport()
|
||||||
return true
|
return true
|
||||||
|
|||||||
@@ -120,6 +120,10 @@ class MainViewModel(
|
|||||||
settings.useDynamicTheme?.let { dynamicColor ->
|
settings.useDynamicTheme?.let { dynamicColor ->
|
||||||
prefs.dynamicColor.update(dynamicColor)
|
prefs.dynamicColor.update(dynamicColor)
|
||||||
}
|
}
|
||||||
|
settings.usePrereleases?.let { prereleases ->
|
||||||
|
prefs.useManagerPrereleases.update(prereleases)
|
||||||
|
prefs.usePatchesPrereleases.update(prereleases)
|
||||||
|
}
|
||||||
settings.apiUrl?.let { api ->
|
settings.apiUrl?.let { api ->
|
||||||
prefs.api.update(api.removeSuffix("/"))
|
prefs.api.update(api.removeSuffix("/"))
|
||||||
}
|
}
|
||||||
@@ -143,7 +147,7 @@ class MainViewModel(
|
|||||||
settings.keystore?.let { keystore ->
|
settings.keystore?.let { keystore ->
|
||||||
val keystoreBytes = Base64.decode(keystore, Base64.DEFAULT)
|
val keystoreBytes = Base64.decode(keystore, Base64.DEFAULT)
|
||||||
keystoreManager.import(
|
keystoreManager.import(
|
||||||
"ReVanced",
|
"alias",
|
||||||
settings.keystorePassword,
|
settings.keystorePassword,
|
||||||
keystoreBytes.inputStream()
|
keystoreBytes.inputStream()
|
||||||
)
|
)
|
||||||
@@ -159,6 +163,7 @@ class MainViewModel(
|
|||||||
val keystorePassword: String,
|
val keystorePassword: String,
|
||||||
val themeMode: Int? = null,
|
val themeMode: Int? = null,
|
||||||
val useDynamicTheme: Boolean? = null,
|
val useDynamicTheme: Boolean? = null,
|
||||||
|
val usePrereleases: Boolean? = null,
|
||||||
val apiUrl: String? = null,
|
val apiUrl: String? = null,
|
||||||
val experimentalPatchesEnabled: Boolean? = null,
|
val experimentalPatchesEnabled: Boolean? = null,
|
||||||
val patchesAutoUpdate: Boolean? = null,
|
val patchesAutoUpdate: Boolean? = null,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ class UpdatesSettingsViewModel(
|
|||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
val managerAutoUpdates = prefs.managerAutoUpdates
|
val managerAutoUpdates = prefs.managerAutoUpdates
|
||||||
val showManagerUpdateDialogOnLaunch = prefs.showManagerUpdateDialogOnLaunch
|
val showManagerUpdateDialogOnLaunch = prefs.showManagerUpdateDialogOnLaunch
|
||||||
|
val useManagerPrereleases = prefs.useManagerPrereleases
|
||||||
|
|
||||||
|
|
||||||
val isConnected: Boolean
|
val isConnected: Boolean
|
||||||
get() = network.isConnected()
|
get() = network.isConnected()
|
||||||
|
|||||||
@@ -368,6 +368,8 @@
|
|||||||
<string name="manual_update_check_description">Manually check for updates</string>
|
<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">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="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">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>
|
||||||
@@ -435,6 +437,8 @@
|
|||||||
<string name="auto_update">Auto update</string>
|
<string name="auto_update">Auto update</string>
|
||||||
<string name="add_patches">Add patches</string>
|
<string name="add_patches">Add patches</string>
|
||||||
<string name="auto_update_description">Automatically update when a new version is available</string>
|
<string name="auto_update_description">Automatically update when a new version is available</string>
|
||||||
|
<string name="patches_prereleases">Prereleases</string>
|
||||||
|
<string name="patches_prereleases_description">Use prerelease versions</string>
|
||||||
<string name="patches_url">Patches URL</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).\n\nClick on the patches to see more details.</string>
|
||||||
<string name="incompatible_patch">Incompatible patch</string>
|
<string name="incompatible_patch">Incompatible patch</string>
|
||||||
|
|||||||
Reference in New Issue
Block a user