mirror of
https://github.com/revanced/revanced-manager-compose-old.git
synced 2025-04-30 06:24:28 +02:00
feat: API
This commit is contained in:
parent
bd2424611d
commit
1211fc42a9
@ -114,7 +114,7 @@ dependencies {
|
|||||||
val ktorVersion = "2.0.3"
|
val ktorVersion = "2.0.3"
|
||||||
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
implementation("io.ktor:ktor-client-core:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-android:$ktorVersion")
|
implementation("io.ktor:ktor-client-android:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-cio:$ktorVersion")
|
implementation("io.ktor:ktor-client-okhttp:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
|
implementation("io.ktor:ktor-client-content-negotiation:$ktorVersion")
|
||||||
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
|
||||||
|
|
||||||
|
@ -1,42 +1,25 @@
|
|||||||
package app.revanced.manager.api
|
package app.revanced.manager.api
|
||||||
|
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import app.revanced.manager.dto.github.APIRelease
|
import app.revanced.manager.dto.github.Assets
|
||||||
import app.revanced.manager.preferences.PreferencesManager
|
import app.revanced.manager.preferences.PreferencesManager
|
||||||
import app.revanced.manager.repository.GitHubRepository
|
import app.revanced.manager.repository.GitHubRepository
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.android.*
|
|
||||||
import io.ktor.client.plugins.*
|
|
||||||
import io.ktor.client.plugins.contentnegotiation.*
|
|
||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import io.ktor.client.statement.*
|
import io.ktor.client.statement.*
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
|
||||||
import io.ktor.util.cio.*
|
import io.ktor.util.cio.*
|
||||||
import io.ktor.utils.io.*
|
import io.ktor.utils.io.*
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
val client = HttpClient(Android) {
|
class API(private val repository: GitHubRepository, private val prefs: PreferencesManager, val client: HttpClient) {
|
||||||
BrowserUserAgent()
|
|
||||||
install(ContentNegotiation) {
|
|
||||||
json(Json {
|
|
||||||
encodeDefaults = true
|
|
||||||
isLenient = true
|
|
||||||
ignoreUnknownKeys = true
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class API(private val repository: GitHubRepository, private val prefs: PreferencesManager) {
|
|
||||||
|
|
||||||
suspend fun findAsset(repo: String, file: String): PatchesAsset {
|
suspend fun findAsset(repo: String, file: String): PatchesAsset {
|
||||||
val release = repository.getLatestRelease(repo)
|
val asset = repository.fetchAssets().tools.findAsset(repo, file) ?: throw MissingAssetException()
|
||||||
val asset = release.assets.findAsset(file) ?: throw MissingAssetException()
|
return PatchesAsset(asset)
|
||||||
return PatchesAsset(release, asset)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun List<APIRelease.Asset>.findAsset(file: String) = find { asset ->
|
private fun List<Assets>.findAsset(repo: String, file: String) = find { asset ->
|
||||||
(asset.name.contains(file) && !asset.name.contains("-sources") && !asset.name.contains("-javadoc"))
|
(asset.name.contains(file) && asset.repository.contains(repo))
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun downloadPatchBundle(workdir: File): File {
|
suspend fun downloadPatchBundle(workdir: File): File {
|
||||||
@ -61,28 +44,26 @@ class API(private val repository: GitHubRepository, private val prefs: Preferenc
|
|||||||
|
|
||||||
suspend fun downloadAsset(
|
suspend fun downloadAsset(
|
||||||
workdir: File,
|
workdir: File,
|
||||||
patchesAsset: PatchesAsset
|
assets: PatchesAsset
|
||||||
): Pair<PatchesAsset, File> {
|
): Pair<PatchesAsset, File> {
|
||||||
val (release, asset) = patchesAsset
|
val out = workdir.resolve("${assets.asset.version}-${assets.asset.name}")
|
||||||
val out = workdir.resolve("${release.tagName}-${asset.name}")
|
|
||||||
if (out.exists()) {
|
if (out.exists()) {
|
||||||
Log.d(
|
Log.d(
|
||||||
"ReVanced Manager",
|
"ReVanced Manager",
|
||||||
"Skipping downloading asset ${asset.name} because it exists in cache!"
|
"Skipping downloading asset ${assets.asset.name} because it exists in cache!"
|
||||||
)
|
)
|
||||||
return patchesAsset to out
|
return assets to out
|
||||||
}
|
}
|
||||||
Log.d("ReVanced Manager", "Downloading asset ${asset.name}")
|
Log.d("ReVanced Manager", "Downloading asset ${assets.asset.name}")
|
||||||
client.get(asset.downloadUrl)
|
client.get(assets.asset.downloadUrl)
|
||||||
.bodyAsChannel()
|
.bodyAsChannel()
|
||||||
.copyAndClose(out.writeChannel())
|
.copyAndClose(out.writeChannel())
|
||||||
|
|
||||||
return patchesAsset to out
|
return assets to out
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data class PatchesAsset(
|
data class PatchesAsset(
|
||||||
val release: APIRelease,
|
val asset: Assets
|
||||||
val asset: APIRelease.Asset
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,16 +1,29 @@
|
|||||||
package app.revanced.manager.di
|
package app.revanced.manager.di
|
||||||
|
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.engine.cio.*
|
import io.ktor.client.engine.okhttp.*
|
||||||
import io.ktor.client.plugins.*
|
import io.ktor.client.plugins.*
|
||||||
import io.ktor.client.plugins.contentnegotiation.*
|
import io.ktor.client.plugins.contentnegotiation.*
|
||||||
import io.ktor.serialization.kotlinx.json.*
|
import io.ktor.serialization.kotlinx.json.*
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.Dns
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
import java.net.Inet4Address
|
||||||
|
import java.net.InetAddress
|
||||||
|
|
||||||
val httpModule = module {
|
val httpModule = module {
|
||||||
fun provideHttpClient() = HttpClient(CIO) {
|
fun provideHttpClient() = HttpClient(OkHttp) {
|
||||||
|
engine {
|
||||||
|
config {
|
||||||
|
dns(object : Dns {
|
||||||
|
override fun lookup(hostname: String): List<InetAddress> {
|
||||||
|
val addresses = Dns.SYSTEM.lookup(hostname)
|
||||||
|
return addresses.filterIsInstance<Inet4Address>()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
BrowserUserAgent()
|
BrowserUserAgent()
|
||||||
install(ContentNegotiation) {
|
install(ContentNegotiation) {
|
||||||
json(Json {
|
json(Json {
|
||||||
|
@ -1,22 +0,0 @@
|
|||||||
package app.revanced.manager.dto.github
|
|
||||||
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class APICommit(
|
|
||||||
val sha: String,
|
|
||||||
val commit: Object
|
|
||||||
) {
|
|
||||||
@Serializable
|
|
||||||
class Object(
|
|
||||||
val message: String,
|
|
||||||
val author: Author,
|
|
||||||
val committer: Author
|
|
||||||
)
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class Author(
|
|
||||||
val name: String,
|
|
||||||
val date: String
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,11 +0,0 @@
|
|||||||
package app.revanced.manager.dto.github
|
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class APIContributor(
|
|
||||||
@SerialName("login") val username: String,
|
|
||||||
@SerialName("avatar_url") val avatarUrl: String,
|
|
||||||
@SerialName("html_url") val profileUrl: String,
|
|
||||||
)
|
|
@ -1,19 +0,0 @@
|
|||||||
package app.revanced.manager.dto.github
|
|
||||||
|
|
||||||
import kotlinx.serialization.SerialName
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class APIRelease(
|
|
||||||
@SerialName("tag_name") val tagName: String,
|
|
||||||
@SerialName("published_at") val publishedAt: String,
|
|
||||||
val prerelease: Boolean,
|
|
||||||
val assets: List<Asset>,
|
|
||||||
val body: String
|
|
||||||
) {
|
|
||||||
@Serializable
|
|
||||||
data class Asset(
|
|
||||||
@SerialName("browser_download_url") val downloadUrl: String,
|
|
||||||
val name: String
|
|
||||||
)
|
|
||||||
}
|
|
@ -1,32 +1,27 @@
|
|||||||
package app.revanced.manager.repository
|
package app.revanced.manager.repository
|
||||||
|
|
||||||
import app.revanced.manager.dto.github.APICommit
|
import app.revanced.manager.dto.github.Tools
|
||||||
import app.revanced.manager.dto.github.APIContributor
|
import app.revanced.manager.dto.github.Repositories
|
||||||
import app.revanced.manager.dto.github.APIRelease
|
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
import io.ktor.client.call.*
|
import io.ktor.client.call.*
|
||||||
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
|
||||||
|
|
||||||
class GitHubRepository(private val client: HttpClient) {
|
class GitHubRepository(val client: HttpClient) {
|
||||||
suspend fun getLatestRelease(repo: String) = withContext(Dispatchers.IO) {
|
|
||||||
val res: List<APIRelease> = client.get("$baseUrl/$repo/releases") {
|
|
||||||
|
suspend fun fetchAssets() = withContext(Dispatchers.IO) {
|
||||||
|
client.get("$apiUrl/tools") {
|
||||||
parameter("per_page", 1)
|
parameter("per_page", 1)
|
||||||
}.body()
|
}.body() as Tools
|
||||||
res.first()
|
|
||||||
}
|
|
||||||
suspend fun getLatestCommit(repo: String, ref: String) = withContext(Dispatchers.IO) {
|
|
||||||
client.get("$baseUrl/$repo/commits/$ref") {
|
|
||||||
parameter("per_page", 1)
|
|
||||||
}.body() as APICommit
|
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getContributors(org: String, repo: String) = withContext(Dispatchers.IO) {
|
suspend fun fetchContributors() = withContext(Dispatchers.IO) {
|
||||||
client.get("$baseUrl/$org/$repo/contributors").body() as List<APIContributor>
|
client.get("$apiUrl/contributors").body() as Repositories
|
||||||
}
|
}
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
private const val baseUrl = "https://api.github.com/repos"
|
private const val apiUrl = "https://revanced-releases-api.afterst0rm.xyz"
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,40 +1,63 @@
|
|||||||
package app.revanced.manager.ui.viewmodel
|
package app.revanced.manager.ui.viewmodel
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import android.text.format.DateUtils
|
import android.text.format.DateUtils
|
||||||
|
import android.util.Log
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import app.revanced.manager.dto.github.Assets
|
||||||
import app.revanced.manager.repository.GitHubRepository
|
import app.revanced.manager.repository.GitHubRepository
|
||||||
import app.revanced.manager.util.ghManager
|
import app.revanced.manager.util.ghManager
|
||||||
import app.revanced.manager.util.ghPatcher
|
import app.revanced.manager.util.ghPatcher
|
||||||
import kotlinx.coroutines.runBlocking
|
import app.revanced.manager.util.tag
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
class DashboardViewModel(private val repository: GitHubRepository) : ViewModel() {
|
class DashboardViewModel(private val repository: GitHubRepository) : ViewModel() {
|
||||||
var patcherCommitDate by mutableStateOf("")
|
private var _latestPatcherCommit: Assets? by mutableStateOf(null)
|
||||||
private set
|
val patcherCommitDate: String
|
||||||
var managerCommitDate by mutableStateOf("")
|
get() = _latestPatcherCommit?.commitDate ?: "unknown"
|
||||||
private set
|
|
||||||
|
private var _latestManagerCommit: Assets? by mutableStateOf(null)
|
||||||
|
val managerCommitDate: String
|
||||||
|
get() = _latestManagerCommit?.commitDate ?: "unknown"
|
||||||
|
|
||||||
init {
|
init {
|
||||||
runBlocking {
|
fetchLastCommit()
|
||||||
patcherCommitDate = commitDateOf(ghPatcher)
|
}
|
||||||
managerCommitDate = commitDateOf(ghManager)
|
|
||||||
|
private fun fetchLastCommit() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
try {
|
||||||
|
val repo = repository.fetchAssets()
|
||||||
|
for (asset in repo.tools) {
|
||||||
|
when (asset.repository) {
|
||||||
|
ghPatcher -> {
|
||||||
|
_latestPatcherCommit = asset
|
||||||
|
}
|
||||||
|
ghManager -> {
|
||||||
|
_latestManagerCommit = asset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e(tag, "Failed to fetch latest patcher release", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun commitDateOf(repo: String, ref: String = "HEAD"): String {
|
private val Assets.commitDate: String
|
||||||
val commit = repository.getLatestCommit(repo, ref).commit
|
get() = DateUtils.getRelativeTimeSpanString(
|
||||||
return DateUtils.getRelativeTimeSpanString(
|
formatter.parse(timestamp)!!.time,
|
||||||
formatter.parse(commit.committer.date)!!.time,
|
|
||||||
Calendar.getInstance().timeInMillis,
|
Calendar.getInstance().timeInMillis,
|
||||||
DateUtils.MINUTE_IN_MILLIS
|
DateUtils.MINUTE_IN_MILLIS
|
||||||
).toString()
|
).toString()
|
||||||
}
|
|
||||||
|
|
||||||
private companion object {
|
private companion object {
|
||||||
|
@SuppressLint("ConstantLocale")
|
||||||
val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault())
|
val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault())
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user