Compare commits

..

2 Commits

Author SHA1 Message Date
Ushie
95cd097519 bleh 2026-03-01 02:55:15 +03:00
Ushie
d685382277 feat: Display more than the latest changelog 2026-02-27 08:34:40 +03:00
11 changed files with 172 additions and 64 deletions

View File

@@ -1,10 +1,3 @@
# api [1.0.0-dev.2](https://github.com/ReVanced/revanced-manager/compare/api@1.0.0-dev.1...api@1.0.0-dev.2) (2026-02-27)
### Features
* Update to Patcher v22 ([#2939](https://github.com/ReVanced/revanced-manager/issues/2939)) ([8667051](https://github.com/ReVanced/revanced-manager/commit/8667051283f934a32ac7b7cc76178397dc45a0e6))
# api 1.0.0-dev.1 (2026-02-19) # api 1.0.0-dev.1 (2026-02-19)

View File

@@ -1 +1 @@
version = 1.0.0-dev.2 version = 1.0.0-dev.1

View File

@@ -257,6 +257,9 @@ android {
// Desktop AAPT binaries // Desktop AAPT binaries
excludes += "/prebuilt/**" excludes += "/prebuilt/**"
// Reflection symbol list (https://stackoverflow.com/a/41073782/13964629)
excludes += "/**/*.kotlin_builtins"
} }
jniLibs { jniLibs {
// 32-bit x86 is dead // 32-bit x86 is dead

View File

@@ -11,7 +11,4 @@
-dontwarn com.google.j2objc.annotations.* -dontwarn com.google.j2objc.annotations.*
-dontwarn java.awt.** -dontwarn java.awt.**
-dontwarn javax.** -dontwarn javax.**
-dontwarn org.slf4j.** -dontwarn org.slf4j.**
-dontwarn org.immutables.value.Generated
-dontwarn reactor.blockhound.integration.BlockHoundIntegration

View File

@@ -20,16 +20,23 @@ class ReVancedAPI(
) { ) {
private suspend fun apiUrl() = prefs.api.get() private suspend fun apiUrl() = prefs.api.get()
private suspend inline fun <reified T> request(api: String, route: String): APIResponse<T> = private suspend inline fun <reified T> request(
api: String,
route: String,
version: String? = "v4"
): APIResponse<T> =
withContext( withContext(
Dispatchers.IO Dispatchers.IO
) { ) {
client.request { client.request {
url("$api/v4/$route") url("$api/$version/$route")
} }
} }
private suspend inline fun <reified T> request(route: String) = request<T>(apiUrl(), route) private suspend inline fun <reified T> request(
route: String,
version: String = "v4"
) = request<T>(apiUrl(), route, version)
suspend fun getAnnouncements() = request<List<ReVancedAnnouncement>>("announcements") suspend fun getAnnouncements() = request<List<ReVancedAnnouncement>>("announcements")
@@ -41,6 +48,8 @@ class ReVancedAPI(
suspend fun getLatestAppInfo() = suspend fun getLatestAppInfo() =
request<ReVancedAsset>("manager?prerelease=${prefs.useManagerPrereleases.get()}") request<ReVancedAsset>("manager?prerelease=${prefs.useManagerPrereleases.get()}")
suspend fun getAppHistory() = request<ReVancedAsset>("patches/history", "dev")
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches?prerelease=${prefs.usePatchesPrereleases.get()}") suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches?prerelease=${prefs.usePatchesPrereleases.get()}")
suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors") suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors")

View File

@@ -7,7 +7,8 @@ import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.NewReleases import androidx.compose.material.icons.outlined.CalendarToday
import androidx.compose.material.icons.outlined.Campaign
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -21,7 +22,7 @@ import app.revanced.manager.ui.component.Markdown
@Composable @Composable
fun Changelog( fun Changelog(
markdown: String, description: String,
version: String, version: String,
publishDate: String publishDate: String
) { ) {
@@ -34,22 +35,32 @@ fun Changelog(
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Icon( Icon(
imageVector = Icons.Outlined.NewReleases, imageVector = Icons.Outlined.Campaign,
contentDescription = null, contentDescription = null,
tint = MaterialTheme.colorScheme.primary, tint = MaterialTheme.colorScheme.primary,
modifier = Modifier modifier = Modifier
.size(32.dp) .size(32.dp)
) )
Text( Text(
"${version.removePrefix("v")} ($publishDate)", version.removePrefix("v"),
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)), style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)),
color = MaterialTheme.colorScheme.primary, color = MaterialTheme.colorScheme.primary,
) )
} }
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.fillMaxWidth()
) {
Tag(
Icons.Outlined.CalendarToday,
publishDate
)
}
Markdown(
description.removeVersionHeaderIfMatches(version),
)
} }
Markdown(
markdown,
)
} }
@Composable @Composable
@@ -70,4 +81,16 @@ private fun Tag(icon: ImageVector, text: String) {
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
) )
} }
}
fun String.removeVersionHeaderIfMatches(version: String): String {
val firstNewlineIndex = indexOf('\n')
if (firstNewlineIndex == -1) return this
val firstLine = substring(0, firstNewlineIndex).trim()
val versionWithoutPrefix = version.removePrefix("v")
if (!firstLine.contains(versionWithoutPrefix)) return this
return substring(firstNewlineIndex + 1).trimStart()
} }

View File

@@ -189,7 +189,7 @@ private fun ColumnScope.Changelog(releaseInfo: ReVancedAsset) {
) )
) { ) {
Changelog( Changelog(
markdown = releaseInfo.description.replace("`", ""), description = releaseInfo.description.replace("`", ""),
version = releaseInfo.version, version = releaseInfo.version,
publishDate = releaseInfo.createdAt.relativeTime(LocalContext.current) publishDate = releaseInfo.createdAt.relativeTime(LocalContext.current)
) )

View File

@@ -1,12 +1,15 @@
package app.revanced.manager.ui.screen.settings.update package app.revanced.manager.ui.screen.settings.update
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -17,10 +20,12 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.network.dto.ReVancedAsset
import app.revanced.manager.ui.component.AppTopBar import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.ColumnWithScrollbar import app.revanced.manager.ui.component.LazyColumnWithScrollbar
import app.revanced.manager.ui.component.LoadingIndicator import app.revanced.manager.ui.component.LoadingIndicator
import app.revanced.manager.ui.component.settings.Changelog import app.revanced.manager.ui.component.settings.Changelog
import app.revanced.manager.ui.viewmodel.ChangelogUiState
import app.revanced.manager.ui.viewmodel.ChangelogsViewModel import app.revanced.manager.ui.viewmodel.ChangelogsViewModel
import app.revanced.manager.util.relativeTime import app.revanced.manager.util.relativeTime
import org.koin.androidx.compose.koinViewModel import org.koin.androidx.compose.koinViewModel
@@ -43,22 +48,61 @@ fun ChangelogsSettingsScreen(
}, },
modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = Modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
) { paddingValues -> ) { paddingValues ->
ColumnWithScrollbar( Box(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize(), .fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally, contentAlignment = Alignment.Center
verticalArrangement = if (vm.releaseInfo == null) Arrangement.Center else Arrangement.Top
) { ) {
vm.releaseInfo?.let { info -> when (val state = vm.state) {
Column(modifier = Modifier.padding(16.dp)) { is ChangelogUiState.Loading -> LoadingIndicator()
Changelog(
markdown = info.description.replace("`", ""), is ChangelogUiState.Error -> Text(
version = info.version, text = state.message,
publishDate = info.createdAt.relativeTime(LocalContext.current) style = MaterialTheme.typography.titleLarge
) )
is ChangelogUiState.Success -> {
if (state.changelogs.isEmpty()) {
Text(
text = stringResource(R.string.no_changelogs_found),
style = MaterialTheme.typography.titleLarge
)
} else {
LazyColumnWithScrollbar(modifier = Modifier.fillMaxSize()) {
items(
items = state.changelogs,
key = { it.version }
) { changelog ->
ChangelogItem(
changelog = changelog,
showDivider = changelog != state.changelogs.last()
)
}
}
}
} }
} ?: LoadingIndicator() }
}
}
}
@Composable
private fun ChangelogItem(
changelog: ReVancedAsset,
showDivider: Boolean
) {
Column(modifier = Modifier.padding(16.dp)) {
Changelog(
description = changelog.description,
version = changelog.version,
publishDate = changelog.createdAt.relativeTime(LocalContext.current)
)
if (showDivider) {
HorizontalDivider(
modifier = Modifier.padding(top = 32.dp),
color = MaterialTheme.colorScheme.outlineVariant
)
} }
} }
} }

View File

@@ -12,18 +12,57 @@ import app.revanced.manager.network.dto.ReVancedAsset
import app.revanced.manager.network.utils.getOrThrow import app.revanced.manager.network.utils.getOrThrow
import app.revanced.manager.util.uiSafe import app.revanced.manager.util.uiSafe
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.datetime.LocalDateTime
import kotlinx.serialization.json.Json
sealed interface ChangelogUiState {
data object Loading : ChangelogUiState
data class Success(val changelogs: List<ReVancedAsset>) : ChangelogUiState
data class Error(val message: String) : ChangelogUiState
}
class ChangelogsViewModel( class ChangelogsViewModel(
private val api: ReVancedAPI, private val api: ReVancedAPI,
private val app: Application, private val app: Application,
) : ViewModel() { ) : ViewModel() {
var releaseInfo: ReVancedAsset? by mutableStateOf(null) val mock = listOf(
ReVancedAsset(
version = "v1.25.2",
createdAt = LocalDateTime.parse("2025-07-07T13:01:12"),
description = """
## [1.25.2](https://github.com/ReVanced/revanced-manager/compare/v1.25.0...v1.25.1) (2025-07-07)
### Bug Fixes
* Disable Impeller Flutter render engine correctly to fix rendering issues
""".trimIndent(),
downloadUrl = "https://github.com/ReVanced/revanced-manager/releases/download/v1.25.1/revanced-manager-1.25.1.apk"
),
ReVancedAsset(
version = "v1.25.1",
createdAt = LocalDateTime.parse("2025-07-07T13:01:12"),
description = """
## [1.25.1](https://github.com/ReVanced/revanced-manager/compare/v1.25.0...v1.25.1) (2025-07-07)
### Bug Fixes
* Disable Impeller Flutter render engine correctly to fix rendering issues
""".trimIndent(),
downloadUrl = "https://github.com/ReVanced/revanced-manager/releases/download/v1.25.1/revanced-manager-1.25.1.apk"
)
)
var state: ChangelogUiState by mutableStateOf(ChangelogUiState.Loading)
private set private set
init { init {
viewModelScope.launch { viewModelScope.launch {
uiSafe(app, R.string.changelog_download_fail, "Failed to download changelog") { uiSafe(app, R.string.changelog_download_fail, "Failed to download changelog") {
releaseInfo = api.getLatestAppInfo().getOrThrow() api.getLatestAppInfo().getOrThrow()
state = ChangelogUiState.Success(changelogs = mock)
}
if (state is ChangelogUiState.Loading) {
state = ChangelogUiState.Error(app.getString(R.string.changelog_download_fail))
} }
} }
} }

View File

@@ -113,24 +113,24 @@ Second \"item\" text"</string>
<string name="safeguards">Safeguards</string> <string name="safeguards">Safeguards</string>
<string name="patch_compat_check">Disable version compatibility check</string> <string name="patch_compat_check">Disable version compatibility check</string>
<string name="patch_compat_check_description">Do not restrict patches to compatible app versions</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. <string name="patch_compat_check_confirmation">"Selecting incompatible patches can result in a broken app.
Do you want to proceed anyways?</string> Do you want to proceed anyways?"</string>
<string name="suggested_version_safeguard">Require suggested app version</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_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. <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> Do you want to proceed anyways?"</string>
<string name="patch_selection_safeguard">Allow changing patch selection and options</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_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. <string name="patch_selection_safeguard_confirmation">"Changing the selection of patches may cause unexpected issues.
Enable anyways?</string> Enable anyways?"</string>
<string name="universal_patches_safeguard">Allow using universal patches</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_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. <string name="universal_patches_safeguard_confirmation">"Universal patches are not as well tested as those that target specific apps.
Enable anyways?</string> Enable anyways?"</string>
<string name="import_keystore">Import keystore</string> <string name="import_keystore">Import keystore</string>
<string name="import_keystore_description">Import a custom keystore</string> <string name="import_keystore_description">Import a custom keystore</string>
<string name="import_keystore_dialog_title">Enter keystore credentials</string> <string name="import_keystore_dialog_title">Enter keystore credentials</string>
@@ -146,9 +146,9 @@ Enable anyways?</string>
<string name="export_keystore_success">Exported keystore</string> <string name="export_keystore_success">Exported keystore</string>
<string name="regenerate_keystore">Regenerate keystore</string> <string name="regenerate_keystore">Regenerate keystore</string>
<string name="regenerate_keystore_description">Generate a new 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. <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> 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="regenerate_keystore_success">The keystore has been successfully replaced</string>
<string name="import_patch_selection">Import patch selection</string> <string name="import_patch_selection">Import patch selection</string>
<string name="import_patch_selection_description">Import patch selection from a JSON file</string> <string name="import_patch_selection_description">Import patch selection from a JSON file</string>
@@ -273,23 +273,23 @@ You will not be able to update the previously installed apps from this source.</
<string name="patch_selection_reset_toast">Patch selection and options has been reset to recommended defaults</string> <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="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_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. <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 Please use the suggested version: %s
To continue anyway, disable \"Require suggested app version\" in the advanced settings.</string> 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_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. <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> 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. <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> 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="this_version">This version</string>
<string name="universal">Any app</string> <string name="universal">Any app</string>
<string name="search_patches">Search patches</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) <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> It is only compatible with the following version(s): %2$s"</string>
<string name="continue_with_version">Continue with this version?</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="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> <string name="download_application">Download application?</string>
@@ -434,8 +434,8 @@ It is only compatible with the following version(s): %2$s</string>
<string name="save_with_count">Save (%1$s)</string> <string name="save_with_count">Save (%1$s)</string>
<string name="update">Update</string> <string name="update">Update</string>
<string name="empty">Empty</string> <string name="empty">Empty</string>
<string name="installing_message">Tap on <b>Update</b> when prompted. <string name="installing_message">"Tap on <b>Update</b> when prompted.
ReVanced Manager will close when updating.</string> ReVanced Manager will close when updating."</string>
<string name="no_changelogs_found">No changelogs found</string> <string name="no_changelogs_found">No changelogs found</string>
<string name="just_now">Just now</string> <string name="just_now">Just now</string>
<string name="minutes_ago">%sm ago</string> <string name="minutes_ago">%sm ago</string>
@@ -455,9 +455,9 @@ ReVanced Manager will close when updating.</string>
<string name="update_available_dialog_description">A new version of ReVanced Manager (%s) is available.</string> <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="failed_to_download_update">Failed to download update: %s</string>
<string name="download">Download</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. <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> Do you still want to continue?"</string>
<string name="download_update_confirmation">Download update?</string> <string name="download_update_confirmation">Download update?</string>
<string name="no_contributors_found">No contributors found</string> <string name="no_contributors_found">No contributors found</string>
<string name="select">Select</string> <string name="select">Select</string>
@@ -495,9 +495,9 @@ Do you still want to continue?</string>
<string name="patches_prereleases">Use pre-releases</string> <string name="patches_prereleases">Use pre-releases</string>
<string name="patches_prereleases_description">Use pre-release versions of %s</string> <string name="patches_prereleases_description">Use pre-release versions of %s</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). <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> Click on the patches to see more details."</string>
<string name="incompatible_patch">Incompatible patch</string> <string name="incompatible_patch">Incompatible patch</string>
<string name="any_version">Any</string> <string name="any_version">Any</string>
<string name="never_show_again">Never show again</string> <string name="never_show_again">Never show again</string>

View File

@@ -23,7 +23,7 @@ koin = "4.1.1"
ktor = "3.3.3" ktor = "3.3.3"
markdown-renderer = "0.39.0" markdown-renderer = "0.39.0"
fading-edges = "1.0.4" fading-edges = "1.0.4"
kotlin = "2.3.10" kotlin = "2.3.0"
android-gradle-plugin = "8.13.2" android-gradle-plugin = "8.13.2"
dev-tools-gradle-plugin = "2.3.4" dev-tools-gradle-plugin = "2.3.4"
about-libraries = "13.2.1" about-libraries = "13.2.1"