feat: switch to the new api (#75)

This commit is contained in:
Ax333l 2023-08-07 11:03:50 +02:00 committed by GitHub
parent 7e3c31c4b2
commit 3f059d7748
14 changed files with 142 additions and 148 deletions

View File

@ -4,14 +4,13 @@ import app.revanced.manager.data.platform.FileSystem
import app.revanced.manager.data.platform.NetworkInfo import app.revanced.manager.data.platform.NetworkInfo
import app.revanced.manager.domain.repository.* import app.revanced.manager.domain.repository.*
import app.revanced.manager.domain.worker.WorkerRepository import app.revanced.manager.domain.worker.WorkerRepository
import app.revanced.manager.network.api.ManagerAPI import app.revanced.manager.network.api.ReVancedAPI
import org.koin.core.module.dsl.singleOf import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module import org.koin.dsl.module
val repositoryModule = module { val repositoryModule = module {
singleOf(::ReVancedRepository) singleOf(::ReVancedAPI)
singleOf(::GithubRepository) singleOf(::GithubRepository)
singleOf(::ManagerAPI)
singleOf(::FileSystem) singleOf(::FileSystem)
singleOf(::NetworkInfo) singleOf(::NetworkInfo)
singleOf(::PatchBundlePersistenceRepository) singleOf(::PatchBundlePersistenceRepository)

View File

@ -2,20 +2,18 @@ package app.revanced.manager.domain.bundles
import androidx.compose.runtime.Stable import androidx.compose.runtime.Stable
import app.revanced.manager.data.room.bundles.VersionInfo import app.revanced.manager.data.room.bundles.VersionInfo
import app.revanced.manager.domain.bundles.APIPatchBundle.Companion.toBundleAsset
import app.revanced.manager.domain.repository.Assets
import app.revanced.manager.domain.repository.PatchBundlePersistenceRepository import app.revanced.manager.domain.repository.PatchBundlePersistenceRepository
import app.revanced.manager.domain.repository.ReVancedRepository import app.revanced.manager.network.api.ReVancedAPI
import app.revanced.manager.network.dto.Asset import app.revanced.manager.network.api.ReVancedAPI.Extensions.findAssetByType
import app.revanced.manager.network.dto.BundleAsset import app.revanced.manager.network.dto.BundleAsset
import app.revanced.manager.network.dto.BundleInfo import app.revanced.manager.network.dto.BundleInfo
import app.revanced.manager.network.service.HttpService import app.revanced.manager.network.service.HttpService
import app.revanced.manager.network.utils.getOrThrow import app.revanced.manager.network.utils.getOrThrow
import app.revanced.manager.util.ghIntegrations import app.revanced.manager.util.APK_MIMETYPE
import app.revanced.manager.util.ghPatches import app.revanced.manager.util.JAR_MIMETYPE
import io.ktor.client.request.url import io.ktor.client.request.url
import io.ktor.http.Url
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -57,7 +55,11 @@ sealed class RemotePatchBundle(name: String, id: Int, directory: File, val endpo
suspend fun update(): Boolean = withContext(Dispatchers.IO) { suspend fun update(): Boolean = withContext(Dispatchers.IO) {
val info = getLatestInfo() val info = getLatestInfo()
if (hasInstalled() && VersionInfo(info.patches.version, info.integrations.version) == currentVersion()) { if (hasInstalled() && VersionInfo(
info.patches.version,
info.integrations.version
) == currentVersion()
) {
return@withContext false return@withContext false
} }
@ -94,18 +96,24 @@ class JsonPatchBundle(name: String, id: Int, directory: File, endpoint: String)
class APIPatchBundle(name: String, id: Int, directory: File, endpoint: String) : class APIPatchBundle(name: String, id: Int, directory: File, endpoint: String) :
RemotePatchBundle(name, id, directory, endpoint) { RemotePatchBundle(name, id, directory, endpoint) {
private val api: ReVancedRepository by inject() private val api: ReVancedAPI by inject()
override suspend fun getLatestInfo() = api.getAssets().toBundleInfo() override suspend fun getLatestInfo() = coroutineScope {
fun getAssetAsync(repo: String, mime: String) = async(Dispatchers.IO) {
private companion object { api
fun Assets.toBundleInfo(): BundleInfo { .getRelease(repo)
val patches = find(ghPatches, ".jar") .getOrThrow()
val integrations = find(ghIntegrations, ".apk") .let {
BundleAsset(it.metadata.tag, it.findAssetByType(mime).downloadUrl)
return BundleInfo(patches.toBundleAsset(), integrations.toBundleAsset()) }
} }
fun Asset.toBundleAsset() = BundleAsset(version, downloadUrl) val patches = getAssetAsync("revanced-patches", JAR_MIMETYPE)
val integrations = getAssetAsync("revanced-integrations", APK_MIMETYPE)
BundleInfo(
patches.await(),
integrations.await()
)
} }
} }

View File

@ -10,7 +10,7 @@ class PreferencesManager(
val dynamicColor = booleanPreference("dynamic_color", true) val dynamicColor = booleanPreference("dynamic_color", true)
val theme = enumPreference("theme", Theme.SYSTEM) val theme = enumPreference("theme", Theme.SYSTEM)
val api = stringPreference("api_url", "https://releases.revanced.app") val api = stringPreference("api_url", "https://api.revanced.app")
val allowExperimental = booleanPreference("allow_experimental", false) val allowExperimental = booleanPreference("allow_experimental", false)

View File

@ -2,6 +2,7 @@ package app.revanced.manager.domain.repository
import app.revanced.manager.network.service.GithubService import app.revanced.manager.network.service.GithubService
// TODO: delete this when the revanced api adds download count.
class GithubRepository(private val service: GithubService) { class GithubRepository(private val service: GithubService) {
suspend fun getChangelog(repo: String) = service.getChangelog(repo) suspend fun getChangelog(repo: String) = service.getChangelog(repo)
} }

View File

@ -1,25 +0,0 @@
package app.revanced.manager.domain.repository
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.network.api.MissingAssetException
import app.revanced.manager.network.dto.Asset
import app.revanced.manager.network.dto.ReVancedReleases
import app.revanced.manager.network.service.ReVancedService
import app.revanced.manager.network.utils.getOrThrow
class ReVancedRepository(
private val service: ReVancedService,
private val prefs: PreferencesManager
) {
private suspend fun apiUrl() = prefs.api.get()
suspend fun getContributors() = service.getContributors(apiUrl())
suspend fun getAssets() = Assets(service.getAssets(apiUrl()).getOrThrow())
}
class Assets(private val releases: ReVancedReleases): List<Asset> by releases.tools {
fun find(repo: String, file: String) = find { asset ->
asset.name.contains(file) && asset.repository.contains(repo)
} ?: throw MissingAssetException()
}

View File

@ -1,43 +0,0 @@
package app.revanced.manager.network.api
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import app.revanced.manager.domain.repository.Assets
import app.revanced.manager.domain.repository.ReVancedRepository
import app.revanced.manager.network.dto.Asset
import app.revanced.manager.network.service.HttpService
import app.revanced.manager.util.*
import io.ktor.client.plugins.onDownload
import io.ktor.client.request.url
import java.io.File
// TODO: merge ReVancedRepository into this class
class ManagerAPI(
private val http: HttpService,
private val revancedRepository: ReVancedRepository
) {
var downloadProgress: Float? by mutableStateOf(null)
var downloadedSize: Long? by mutableStateOf(null)
var totalSize: Long? by mutableStateOf(null)
private suspend fun downloadAsset(asset: Asset, saveLocation: File) {
http.download(saveLocation) {
url(asset.downloadUrl)
onDownload { bytesSentTotal, contentLength ->
downloadProgress = (bytesSentTotal.toFloat() / contentLength.toFloat())
downloadedSize = bytesSentTotal
totalSize = contentLength
}
}
downloadProgress = null
}
suspend fun downloadManager(location: File) {
val managerAsset = revancedRepository.getAssets().find(ghManager, ".apk")
downloadAsset(managerAsset, location)
}
}
class MissingAssetException : Exception()

View File

@ -0,0 +1,26 @@
package app.revanced.manager.network.api
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.network.dto.Asset
import app.revanced.manager.network.dto.ReVancedLatestRelease
import app.revanced.manager.network.dto.ReVancedRelease
import app.revanced.manager.network.service.ReVancedService
import app.revanced.manager.network.utils.getOrThrow
import app.revanced.manager.network.utils.transform
class ReVancedAPI(
private val service: ReVancedService,
private val prefs: PreferencesManager
) {
private suspend fun apiUrl() = prefs.api.get()
suspend fun getContributors() = service.getContributors(apiUrl()).transform { it.repositories }
suspend fun getRelease(name: String) = service.getRelease(apiUrl(), name).transform { it.release }
companion object Extensions {
fun ReVancedRelease.findAssetByType(mime: String) = assets.singleOrNull { it.contentType == mime } ?: throw MissingAssetException(mime)
}
}
class MissingAssetException(type: String) : Exception("No asset with type $type")

View File

@ -4,18 +4,18 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
@Serializable @Serializable
class ReVancedRepositories( data class ReVancedGitRepositories(
@SerialName("repositories") val repositories: List<ReVancedRepository>, val repositories: List<ReVancedGitRepository>,
) )
@Serializable @Serializable
class ReVancedRepository( data class ReVancedGitRepository(
@SerialName("name") val name: String, val name: String,
@SerialName("contributors") val contributors: List<ReVancedContributor>, val contributors: List<ReVancedContributor>,
) )
@Serializable @Serializable
class ReVancedContributor( data class ReVancedContributor(
@SerialName("login") val username: String, val username: String,
@SerialName("avatar_url") val avatarUrl: String, @SerialName("avatar_url") val avatarUrl: String,
) )

View File

@ -0,0 +1,32 @@
package app.revanced.manager.network.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class ReVancedLatestRelease(
val release: ReVancedRelease,
)
@Serializable
data class ReVancedRelease(
val metadata: ReVancedReleaseMeta,
val assets: List<Asset>
)
@Serializable
data class ReVancedReleaseMeta(
@SerialName("tag_name") val tag: String,
val name: String,
val draft: Boolean,
val prerelease: Boolean,
@SerialName("created_at") val createdAt: String,
@SerialName("published_at") val publishedAt: String
)
@Serializable
data class Asset(
val name: String,
@SerialName("browser_download_url") val downloadUrl: String,
@SerialName("content_type") val contentType: String
)

View File

@ -1,20 +0,0 @@
package app.revanced.manager.network.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class ReVancedReleases(
@SerialName("tools") val tools: List<Asset>,
)
@Serializable
class Asset(
@SerialName("repository") val repository: String,
@SerialName("version") val version: String,
@SerialName("timestamp") val timestamp: String,
@SerialName("name") val name: String,
@SerialName("size") val size: String?,
@SerialName("browser_download_url") val downloadUrl: String,
@SerialName("content_type") val content_type: String
)

View File

@ -1,11 +1,8 @@
package app.revanced.manager.network.service package app.revanced.manager.network.service
import app.revanced.manager.network.api.MissingAssetException import app.revanced.manager.network.dto.ReVancedLatestRelease
import app.revanced.manager.network.dto.Asset import app.revanced.manager.network.dto.ReVancedGitRepositories
import app.revanced.manager.network.dto.ReVancedReleases
import app.revanced.manager.network.dto.ReVancedRepositories
import app.revanced.manager.network.utils.APIResponse import app.revanced.manager.network.utils.APIResponse
import app.revanced.manager.network.utils.getOrThrow
import io.ktor.client.request.* import io.ktor.client.request.*
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
@ -13,20 +10,17 @@ import kotlinx.coroutines.withContext
class ReVancedService( class ReVancedService(
private val client: HttpService, private val client: HttpService,
) { ) {
suspend fun getAssets(api: String): APIResponse<ReVancedReleases> { suspend fun getRelease(api: String, repo: String): APIResponse<ReVancedLatestRelease> =
return withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
client.request { client.request {
url("$api/tools") url("$api/v2/$repo/releases/latest")
} }
} }
}
suspend fun getContributors(api: String): APIResponse<ReVancedRepositories> { suspend fun getContributors(api: String): APIResponse<ReVancedGitRepositories> =
return withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
client.request { client.request {
url("$api/contributors") url("$api/contributors")
} }
} }
}
} }

View File

@ -56,7 +56,7 @@ fun UpdateProgressScreen(
), style = MaterialTheme.typography.headlineMedium ), style = MaterialTheme.typography.headlineMedium
) )
LinearProgressIndicator( LinearProgressIndicator(
progress = vm.downloadProgress / 100f, progress = vm.downloadProgress,
modifier = Modifier modifier = Modifier
.padding(vertical = 16.dp) .padding(vertical = 16.dp)
.fillMaxWidth() .fillMaxWidth()
@ -66,7 +66,7 @@ fun UpdateProgressScreen(
vm.totalSize.div( vm.totalSize.div(
1000000 1000000
) )
} MB (${vm.downloadProgress.toInt()}%)" else stringResource(R.string.installing_message), } MB (${vm.downloadProgress.times(100).toInt()}%)" else stringResource(R.string.installing_message),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.outline, color = MaterialTheme.colorScheme.outline,
modifier = Modifier.align(Alignment.CenterHorizontally), modifier = Modifier.align(Alignment.CenterHorizontally),

View File

@ -3,22 +3,21 @@ package app.revanced.manager.ui.viewmodel
import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateListOf
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import app.revanced.manager.domain.repository.ReVancedRepository import app.revanced.manager.network.api.ReVancedAPI
import app.revanced.manager.network.dto.ReVancedGitRepository
import app.revanced.manager.network.utils.getOrNull import app.revanced.manager.network.utils.getOrNull
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
class ContributorViewModel(private val repository: ReVancedRepository): ViewModel() { class ContributorViewModel(private val reVancedAPI: ReVancedAPI) : ViewModel() {
val repositories = mutableStateListOf<app.revanced.manager.network.dto.ReVancedRepository>() val repositories = mutableStateListOf<ReVancedGitRepository>()
init { init {
viewModelScope.launch { viewModelScope.launch {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) { reVancedAPI.getContributors().getOrNull() }?.let(
val repos = repository.getContributors().getOrNull()?.repositories repositories::addAll
withContext(Dispatchers.Main) { )
if (repos != null) { repositories.addAll(repos) }
}
}
} }
} }
} }

View File

@ -8,24 +8,36 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.network.api.ManagerAPI import app.revanced.manager.network.api.ReVancedAPI
import app.revanced.manager.network.api.ReVancedAPI.Extensions.findAssetByType
import app.revanced.manager.network.service.HttpService
import app.revanced.manager.network.utils.getOrThrow
import app.revanced.manager.util.APK_MIMETYPE
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import app.revanced.manager.util.PM import app.revanced.manager.util.PM
import app.revanced.manager.util.uiSafe import app.revanced.manager.util.uiSafe
import io.ktor.client.plugins.onDownload
import io.ktor.client.request.url
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
class UpdateProgressViewModel( class UpdateProgressViewModel(
app: Application, app: Application,
private val managerAPI: ManagerAPI, private val reVancedAPI: ReVancedAPI,
private val http: HttpService,
private val pm: PM private val pm: PM
) : ViewModel() { ) : ViewModel() {
var downloadedSize by mutableStateOf(0L)
private set
var totalSize by mutableStateOf(0L)
private set
val downloadProgress by derivedStateOf {
if (downloadedSize == 0L || totalSize == 0L) return@derivedStateOf 0f
val downloadProgress by derivedStateOf { managerAPI.downloadProgress?.times(100) ?: 0f } downloadedSize.toFloat() / totalSize.toFloat()
val downloadedSize by derivedStateOf { managerAPI.downloadedSize ?: 0L } }
val totalSize by derivedStateOf { managerAPI.totalSize ?: 0L } val isInstalling by derivedStateOf { downloadProgress >= 1 }
val isInstalling by derivedStateOf { downloadProgress >= 100 }
var finished by mutableStateOf(false) var finished by mutableStateOf(false)
private set private set
@ -33,7 +45,18 @@ class UpdateProgressViewModel(
private val job = viewModelScope.launch { private val job = viewModelScope.launch {
uiSafe(app, R.string.download_manager_failed, "Failed to download manager") { uiSafe(app, R.string.download_manager_failed, "Failed to download manager") {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
managerAPI.downloadManager(location) val asset = reVancedAPI
.getRelease("revanced-manager")
.getOrThrow()
.findAssetByType(APK_MIMETYPE)
http.download(location) {
url(asset.downloadUrl)
onDownload { bytesSentTotal, contentLength ->
downloadedSize = bytesSentTotal
totalSize = contentLength
}
}
} }
finished = true finished = true
} }