mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-03-13 08:41:57 +08:00
Compare commits
8 Commits
feat/updat
...
v1.26.0-de
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e115e8cd90 | ||
|
|
2270f329de | ||
|
|
55f0cdebd5 | ||
|
|
398ca006b6 | ||
|
|
d368c0ff6f | ||
|
|
1ba11b3021 | ||
|
|
d29e736c4a | ||
|
|
896d8cc874 |
1
.github/workflows/build_pull_request.yml
vendored
1
.github/workflows/build_pull_request.yml
vendored
@@ -29,3 +29,4 @@ jobs:
|
||||
path: |
|
||||
app/build/outputs/apk/release/revanced-manager*.apk
|
||||
app/build/outputs/apk/release/revanced-manager*.apk.asc
|
||||
archive: false
|
||||
|
||||
@@ -1,3 +1,32 @@
|
||||
# app [1.26.0-dev.32](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.31...v1.26.0-dev.32) (2026-03-08)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* migrate to API v5 ([2270f32](https://github.com/ReVanced/revanced-manager/commit/2270f329de25376eb9f0ce196994cc841977ff2a))
|
||||
|
||||
# app [1.26.0-dev.31](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.30...v1.26.0-dev.31) (2026-03-04)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Don't mark all announcements as read on first launch, and set the correct default tags ([398ca00](https://github.com/ReVanced/revanced-manager/commit/398ca006b637048e6280edaf9685800f7638e6ba))
|
||||
|
||||
# app [1.26.0-dev.30](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.29...v1.26.0-dev.30) (2026-03-02)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* Update AAPT2 to built-tools v35. ([1ba11b3](https://github.com/ReVanced/revanced-manager/commit/1ba11b30218be263a56f6512b968f5e3dded9daf))
|
||||
|
||||
# app [1.26.0-dev.29](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.28...v1.26.0-dev.29) (2026-02-28)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* Exclude desktop AAPT and disable R8 optimizations ([6ce5b67](https://github.com/ReVanced/revanced-manager/commit/6ce5b672feb677eccafffb6a04bdf5513f6d62fa))
|
||||
* unexclude other destabilizers ([2c3d436](https://github.com/ReVanced/revanced-manager/commit/2c3d436b258f100feea0b9864b7131ae2c5bda79))
|
||||
|
||||
# app [1.26.0-dev.28](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.27...v1.26.0-dev.28) (2026-02-28)
|
||||
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
version = 1.26.0-dev.28
|
||||
version = 1.26.0-dev.32
|
||||
|
||||
@@ -2,7 +2,6 @@ package app.revanced.manager.domain.repository
|
||||
|
||||
import app.revanced.manager.network.api.ReVancedAPI
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncement
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncementTag
|
||||
import app.revanced.manager.network.utils.getOrNull
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
@@ -12,7 +11,6 @@ class AnnouncementRepository(
|
||||
) {
|
||||
private val mutex = Mutex()
|
||||
private var cachedAnnouncements: List<ReVancedAnnouncement>? = null
|
||||
private var cachedTags: List<ReVancedAnnouncementTag>? = null
|
||||
|
||||
suspend fun getAnnouncements(forceRefresh: Boolean = false): List<ReVancedAnnouncement>? {
|
||||
mutex.withLock {
|
||||
@@ -22,13 +20,4 @@ class AnnouncementRepository(
|
||||
return cachedAnnouncements
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getTags(forceRefresh: Boolean = false): List<ReVancedAnnouncementTag>? {
|
||||
mutex.withLock {
|
||||
if (cachedTags == null || forceRefresh) {
|
||||
cachedTags = api.getAnnouncementTags().getOrNull()
|
||||
}
|
||||
return cachedTags
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package app.revanced.manager.network.api
|
||||
|
||||
import app.revanced.manager.BuildConfig
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.manager.base.Preference
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncement
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncementTag
|
||||
import app.revanced.manager.network.dto.ReVancedAsset
|
||||
import app.revanced.manager.network.dto.ReVancedGitRepository
|
||||
import app.revanced.manager.network.dto.ReVancedInfo
|
||||
@@ -25,7 +25,7 @@ class ReVancedAPI(
|
||||
Dispatchers.IO
|
||||
) {
|
||||
client.request {
|
||||
url("$api/v4/$route")
|
||||
url("$api/v5/$route")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,17 +33,19 @@ class ReVancedAPI(
|
||||
|
||||
suspend fun getAnnouncements() = request<List<ReVancedAnnouncement>>("announcements")
|
||||
|
||||
suspend fun getAnnouncementTags() = request<List<ReVancedAnnouncementTag>>("announcements/tags")
|
||||
|
||||
suspend fun getAppUpdate() =
|
||||
getLatestAppInfo().getOrThrow().takeIf { it.version.removePrefix("v") != BuildConfig.VERSION_NAME }
|
||||
|
||||
suspend fun getLatestAppInfo() =
|
||||
request<ReVancedAsset>("manager?prerelease=${prefs.useManagerPrereleases.get()}")
|
||||
request<ReVancedAsset>("manager${prefs.useManagerPrereleases.prereleaseString()}")
|
||||
|
||||
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches?prerelease=${prefs.usePatchesPrereleases.get()}")
|
||||
suspend fun getPatchesUpdate() = request<ReVancedAsset>("patches${prefs.usePatchesPrereleases.prereleaseString()}")
|
||||
|
||||
suspend fun getContributors() = request<List<ReVancedGitRepository>>("contributors")
|
||||
|
||||
suspend fun getInfo(api: String? = null) = request<ReVancedInfo>(api ?: apiUrl(), "about")
|
||||
|
||||
private companion object {
|
||||
suspend fun Preference<Boolean>.prereleaseString() = if (get()) "/prerelease" else ""
|
||||
}
|
||||
}
|
||||
@@ -2,9 +2,12 @@ package app.revanced.manager.network.dto
|
||||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.datetime.LocalDateTime
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import kotlinx.serialization.SerialName
|
||||
import kotlinx.serialization.Serializable
|
||||
import kotlin.time.Clock
|
||||
|
||||
@Parcelize
|
||||
@Serializable
|
||||
@@ -14,10 +17,11 @@ data class ReVancedAnnouncement(
|
||||
val title: String,
|
||||
val content: String,
|
||||
val tags: List<String>,
|
||||
val attachments: List<String>,
|
||||
@SerialName("created_at")
|
||||
val createdAt: LocalDateTime,
|
||||
@SerialName("archived_at")
|
||||
val archivedAt: LocalDateTime,
|
||||
val archivedAt: LocalDateTime?,
|
||||
val level: Int,
|
||||
) : Parcelable
|
||||
) : Parcelable {
|
||||
val isArchived get() = archivedAt?.toInstant(TimeZone.UTC)?.let { it < Clock.System.now() } ?: false
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
package app.revanced.manager.network.dto
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class ReVancedAnnouncementTag(
|
||||
val name: String
|
||||
)
|
||||
@@ -50,10 +50,7 @@ import app.revanced.manager.ui.component.LoadingIndicator
|
||||
import app.revanced.manager.ui.viewmodel.AnnouncementsViewModel
|
||||
import app.revanced.manager.util.relativeTime
|
||||
import app.revanced.manager.util.transparentListItemColors
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
import org.koin.androidx.compose.koinViewModel
|
||||
import kotlin.time.Clock
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
@@ -64,7 +61,7 @@ fun AnnouncementsScreen(
|
||||
) {
|
||||
val scrollBehavior = TopAppBarDefaults.pinnedScrollBehavior()
|
||||
var showFilterSheet by rememberSaveable { mutableStateOf(false) }
|
||||
val tags by vm.tags.collectAsStateWithLifecycle()
|
||||
val tags by vm.tags.collectAsStateWithLifecycle(null)
|
||||
val selectedTags by vm.selectedTags.getAsState()
|
||||
val showArchived by vm.showArchived.collectAsStateWithLifecycle()
|
||||
val announcements by vm.announcements.collectAsStateWithLifecycle(emptyList())
|
||||
@@ -129,6 +126,7 @@ fun AnnouncementsScreen(
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
|
||||
AnnouncementCard(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
@@ -141,7 +139,7 @@ fun AnnouncementsScreen(
|
||||
author = announcement.author,
|
||||
content = announcement.content,
|
||||
unread = announcement.id !in readAnnouncements,
|
||||
archived = announcement.archivedAt.toInstant(TimeZone.UTC) < Clock.System.now()
|
||||
archived = announcement.isArchived
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -161,7 +159,7 @@ fun AnnouncementsScreen(
|
||||
@Composable
|
||||
private fun FilterBottomSheet(
|
||||
onDismissRequest: () -> Unit,
|
||||
tags: List<String>,
|
||||
tags: Set<String>,
|
||||
selectedTags: Set<String>,
|
||||
showArchived: Boolean,
|
||||
onShowArchivedChange: (Boolean) -> Unit,
|
||||
|
||||
@@ -6,15 +6,12 @@ import app.revanced.manager.data.platform.NetworkInfo
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.repository.AnnouncementRepository
|
||||
import app.revanced.manager.network.dto.ReVancedAnnouncement
|
||||
import kotlin.time.Clock
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.combine
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.TimeZone
|
||||
import kotlinx.datetime.toInstant
|
||||
|
||||
class AnnouncementsViewModel(
|
||||
private val announcementRepository: AnnouncementRepository,
|
||||
@@ -23,37 +20,26 @@ class AnnouncementsViewModel(
|
||||
) : ViewModel() {
|
||||
private val allAnnouncements = MutableStateFlow<List<ReVancedAnnouncement>?>(null)
|
||||
|
||||
private val _tags = MutableStateFlow<List<String>?>(null)
|
||||
val tags get() = _tags.asStateFlow()
|
||||
val tags = allAnnouncements.map { it?.tags }
|
||||
val selectedTags = preferences.selectedAnnouncementTags
|
||||
val readAnnouncements = preferences.readAnnouncements
|
||||
val showArchived = MutableStateFlow(false)
|
||||
|
||||
val announcements = combine(
|
||||
allAnnouncements,
|
||||
_tags,
|
||||
selectedTags.flow,
|
||||
showArchived
|
||||
) { source, tags, selectedTags, showArchived ->
|
||||
) { source, selectedTags, showArchived ->
|
||||
if (source == null) return@combine null
|
||||
// Only filter by tags that actually exist
|
||||
val availableTags = tags.orEmpty().toSet()
|
||||
val availableTags = source.tags
|
||||
val validSelected = selectedTags.intersect(availableTags)
|
||||
|
||||
val visibleAnnouncements = if (showArchived) {
|
||||
source
|
||||
} else {
|
||||
source.filter { announcement ->
|
||||
announcement.archivedAt.toInstant(TimeZone.UTC) > Clock.System.now()
|
||||
}
|
||||
}
|
||||
source.filter { announcement ->
|
||||
if (!showArchived && announcement.isArchived) return@filter false
|
||||
|
||||
if (validSelected.isEmpty()) {
|
||||
visibleAnnouncements
|
||||
} else {
|
||||
visibleAnnouncements.filter { announcement ->
|
||||
announcement.tags.any(validSelected::contains)
|
||||
}
|
||||
if (!validSelected.isEmpty()) announcement.tags.any(validSelected::contains)
|
||||
else true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +70,6 @@ class AnnouncementsViewModel(
|
||||
viewModelScope.launch {
|
||||
if (!network.isConnected()) {
|
||||
allAnnouncements.value = emptyList()
|
||||
_tags.value = emptyList()
|
||||
return@launch
|
||||
}
|
||||
|
||||
@@ -92,10 +77,14 @@ class AnnouncementsViewModel(
|
||||
announcementRepository.getAnnouncements()?.let {
|
||||
allAnnouncements.value = it
|
||||
}
|
||||
announcementRepository.getTags()?.let {
|
||||
_tags.value = it.map { tag -> tag.name }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private companion object {
|
||||
val List<ReVancedAnnouncement>.tags: Set<String>
|
||||
get() = flatMapTo(
|
||||
mutableSetOf()
|
||||
) { it.tags }
|
||||
}
|
||||
}
|
||||
@@ -98,22 +98,14 @@ class DashboardViewModel(
|
||||
} ?: throw IllegalStateException("Announcements could not be retrieved")
|
||||
|
||||
val readAnnouncements = prefs.readAnnouncements.get()
|
||||
if (readAnnouncements.isEmpty()) {
|
||||
val announcementIds = announcements.mapTo(mutableSetOf()) { it.id }
|
||||
prefs.readAnnouncements.update(announcementIds)
|
||||
return@uiSafe
|
||||
}
|
||||
|
||||
unreadAnnouncement = announcements.firstOrNull { announcement ->
|
||||
val isNotArchived =
|
||||
announcement.archivedAt.toInstant(TimeZone.UTC) > Clock.System.now()
|
||||
|
||||
val hasRelevantTag = "revanced" in announcement.tags ||
|
||||
"manager" in announcement.tags
|
||||
|
||||
val hasRelevantTag = announcement.tags.any {
|
||||
it == "✨ ReVanced" || it == "💊 Manager"
|
||||
}
|
||||
val isUnread = announcement.id !in readAnnouncements
|
||||
|
||||
isNotArchived && hasRelevantTag && isUnread
|
||||
!announcement.isArchived && hasRelevantTag && isUnread
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user