From 1211fc42a9f095737cffccee0d0e9de44fe32c0d Mon Sep 17 00:00:00 2001 From: Canny Date: Thu, 6 Oct 2022 20:19:25 +0300 Subject: [PATCH] feat: API --- app/build.gradle.kts | 2 +- .../main/java/app/revanced/manager/api/API.kt | 51 ++++++------------- .../app/revanced/manager/di/HttpModule.kt | 17 ++++++- .../revanced/manager/dto/github/ApiCommit.kt | 22 -------- .../manager/dto/github/ApiContributor.kt | 11 ---- .../revanced/manager/dto/github/ApiRelease.kt | 19 ------- .../manager/repository/GitHubRepository.kt | 27 ++++------ .../ui/viewmodel/DashboardViewModel.kt | 51 ++++++++++++++----- 8 files changed, 80 insertions(+), 120 deletions(-) delete mode 100644 app/src/main/java/app/revanced/manager/dto/github/ApiCommit.kt delete mode 100644 app/src/main/java/app/revanced/manager/dto/github/ApiContributor.kt delete mode 100644 app/src/main/java/app/revanced/manager/dto/github/ApiRelease.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index f484228..b425da2 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -114,7 +114,7 @@ dependencies { val ktorVersion = "2.0.3" implementation("io.ktor:ktor-client-core:$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-serialization-kotlinx-json:$ktorVersion") diff --git a/app/src/main/java/app/revanced/manager/api/API.kt b/app/src/main/java/app/revanced/manager/api/API.kt index dada585..9e3581a 100644 --- a/app/src/main/java/app/revanced/manager/api/API.kt +++ b/app/src/main/java/app/revanced/manager/api/API.kt @@ -1,42 +1,25 @@ package app.revanced.manager.api 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.repository.GitHubRepository 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.statement.* -import io.ktor.serialization.kotlinx.json.* import io.ktor.util.cio.* import io.ktor.utils.io.* -import kotlinx.serialization.json.Json import java.io.File -val client = HttpClient(Android) { - BrowserUserAgent() - install(ContentNegotiation) { - json(Json { - encodeDefaults = true - isLenient = true - ignoreUnknownKeys = true - }) - } -} - -class API(private val repository: GitHubRepository, private val prefs: PreferencesManager) { +class API(private val repository: GitHubRepository, private val prefs: PreferencesManager, val client: HttpClient) { suspend fun findAsset(repo: String, file: String): PatchesAsset { - val release = repository.getLatestRelease(repo) - val asset = release.assets.findAsset(file) ?: throw MissingAssetException() - return PatchesAsset(release, asset) + val asset = repository.fetchAssets().tools.findAsset(repo, file) ?: throw MissingAssetException() + return PatchesAsset(asset) } - private fun List.findAsset(file: String) = find { asset -> - (asset.name.contains(file) && !asset.name.contains("-sources") && !asset.name.contains("-javadoc")) + private fun List.findAsset(repo: String, file: String) = find { asset -> + (asset.name.contains(file) && asset.repository.contains(repo)) } suspend fun downloadPatchBundle(workdir: File): File { @@ -61,29 +44,27 @@ class API(private val repository: GitHubRepository, private val prefs: Preferenc suspend fun downloadAsset( workdir: File, - patchesAsset: PatchesAsset + assets: PatchesAsset ): Pair { - val (release, asset) = patchesAsset - val out = workdir.resolve("${release.tagName}-${asset.name}") + val out = workdir.resolve("${assets.asset.version}-${assets.asset.name}") if (out.exists()) { Log.d( "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}") - client.get(asset.downloadUrl) + Log.d("ReVanced Manager", "Downloading asset ${assets.asset.name}") + client.get(assets.asset.downloadUrl) .bodyAsChannel() .copyAndClose(out.writeChannel()) - return patchesAsset to out + return assets to out } } - data class PatchesAsset( - val release: APIRelease, - val asset: APIRelease.Asset - ) +data class PatchesAsset( + val asset: Assets +) class MissingAssetException : Exception() \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/di/HttpModule.kt b/app/src/main/java/app/revanced/manager/di/HttpModule.kt index 60206d0..b883a4d 100644 --- a/app/src/main/java/app/revanced/manager/di/HttpModule.kt +++ b/app/src/main/java/app/revanced/manager/di/HttpModule.kt @@ -1,16 +1,29 @@ package app.revanced.manager.di 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.contentnegotiation.* import io.ktor.serialization.kotlinx.json.* import kotlinx.serialization.json.Json +import okhttp3.Dns import org.koin.core.module.dsl.singleOf import org.koin.dsl.module +import java.net.Inet4Address +import java.net.InetAddress val httpModule = module { - fun provideHttpClient() = HttpClient(CIO) { + fun provideHttpClient() = HttpClient(OkHttp) { + engine { + config { + dns(object : Dns { + override fun lookup(hostname: String): List { + val addresses = Dns.SYSTEM.lookup(hostname) + return addresses.filterIsInstance() + } + }) + } + } BrowserUserAgent() install(ContentNegotiation) { json(Json { diff --git a/app/src/main/java/app/revanced/manager/dto/github/ApiCommit.kt b/app/src/main/java/app/revanced/manager/dto/github/ApiCommit.kt deleted file mode 100644 index 78a6eb7..0000000 --- a/app/src/main/java/app/revanced/manager/dto/github/ApiCommit.kt +++ /dev/null @@ -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 - ) -} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/dto/github/ApiContributor.kt b/app/src/main/java/app/revanced/manager/dto/github/ApiContributor.kt deleted file mode 100644 index c42d680..0000000 --- a/app/src/main/java/app/revanced/manager/dto/github/ApiContributor.kt +++ /dev/null @@ -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, -) \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/dto/github/ApiRelease.kt b/app/src/main/java/app/revanced/manager/dto/github/ApiRelease.kt deleted file mode 100644 index a878e06..0000000 --- a/app/src/main/java/app/revanced/manager/dto/github/ApiRelease.kt +++ /dev/null @@ -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, - val body: String -) { - @Serializable - data class Asset( - @SerialName("browser_download_url") val downloadUrl: String, - val name: String - ) -} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/repository/GitHubRepository.kt b/app/src/main/java/app/revanced/manager/repository/GitHubRepository.kt index bc24b38..38baede 100644 --- a/app/src/main/java/app/revanced/manager/repository/GitHubRepository.kt +++ b/app/src/main/java/app/revanced/manager/repository/GitHubRepository.kt @@ -1,32 +1,27 @@ package app.revanced.manager.repository -import app.revanced.manager.dto.github.APICommit -import app.revanced.manager.dto.github.APIContributor -import app.revanced.manager.dto.github.APIRelease +import app.revanced.manager.dto.github.Tools +import app.revanced.manager.dto.github.Repositories import io.ktor.client.* import io.ktor.client.call.* import io.ktor.client.request.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext -class GitHubRepository(private val client: HttpClient) { - suspend fun getLatestRelease(repo: String) = withContext(Dispatchers.IO) { - val res: List = client.get("$baseUrl/$repo/releases") { +class GitHubRepository(val client: HttpClient) { + + + suspend fun fetchAssets() = withContext(Dispatchers.IO) { + client.get("$apiUrl/tools") { parameter("per_page", 1) - }.body() - 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 + }.body() as Tools } - suspend fun getContributors(org: String, repo: String) = withContext(Dispatchers.IO) { - client.get("$baseUrl/$org/$repo/contributors").body() as List + suspend fun fetchContributors() = withContext(Dispatchers.IO) { + client.get("$apiUrl/contributors").body() as Repositories } private companion object { - private const val baseUrl = "https://api.github.com/repos" + private const val apiUrl = "https://revanced-releases-api.afterst0rm.xyz" } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt index a268101..5a4aaab 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/DashboardViewModel.kt @@ -1,40 +1,63 @@ package app.revanced.manager.ui.viewmodel +import android.annotation.SuppressLint import android.text.format.DateUtils +import android.util.Log import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue 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.util.ghManager 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.util.* class DashboardViewModel(private val repository: GitHubRepository) : ViewModel() { - var patcherCommitDate by mutableStateOf("") - private set - var managerCommitDate by mutableStateOf("") - private set + private var _latestPatcherCommit: Assets? by mutableStateOf(null) + val patcherCommitDate: String + get() = _latestPatcherCommit?.commitDate ?: "unknown" + + private var _latestManagerCommit: Assets? by mutableStateOf(null) + val managerCommitDate: String + get() = _latestManagerCommit?.commitDate ?: "unknown" init { - runBlocking { - patcherCommitDate = commitDateOf(ghPatcher) - managerCommitDate = commitDateOf(ghManager) + fetchLastCommit() + } + + 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 { - val commit = repository.getLatestCommit(repo, ref).commit - return DateUtils.getRelativeTimeSpanString( - formatter.parse(commit.committer.date)!!.time, + private val Assets.commitDate: String + get() = DateUtils.getRelativeTimeSpanString( + formatter.parse(timestamp)!!.time, Calendar.getInstance().timeInMillis, DateUtils.MINUTE_IN_MILLIS ).toString() - } - private companion object { + @SuppressLint("ConstantLocale") val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.getDefault()) } } \ No newline at end of file