Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Codeberg support #525

Closed
wants to merge 26 commits into from
Closed
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions app/src/main/kotlin/com/apkupdater/data/codeberg/CodeBergApp.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.apkupdater.data.codeberg

data class CodeBergApp(
val packageName: String,
val user: String,
val repo: String,
val extra: Regex? = null
)

val CodeBergApps = listOf(
CodeBergApp("eu.kanade.fabsemanga.psyduck", "fabseman", "fabsemanga"),
CodeBergApp("com.draco.buoy", "s1m", "savertuner")
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.apkupdater.data.codeberg

data class CodeBergAuthor(val avatar_url: String)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.apkupdater.data.codeberg


data class CodeBergRelease(
val name: String,
val prerelease: Boolean,
val assets: List<CodeBergReleaseAsset>,
val tag_name: String,
val author: CodeBergAuthor,
val body: String = ""
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.apkupdater.data.codeberg

data class CodeBergReleaseAsset(
val size: Long,
val browser_download_url: String
)
1 change: 1 addition & 0 deletions app/src/main/kotlin/com/apkupdater/data/ui/Source.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ val IzzySource = Source("F-Droid (Izzy)", R.drawable.ic_izzy)
val AptoideSource = Source("Aptoide", R.drawable.ic_aptoide)
val ApkPureSource = Source("ApkPure", R.drawable.ic_apkpure)
val GitLabSource = Source("GitLab", R.drawable.ic_gitlab)
val CodeBergSource = Source("GitLab", R.drawable.ic_codeberg)
13 changes: 13 additions & 0 deletions app/src/main/kotlin/com/apkupdater/di/MainModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.apkupdater.repository.AptoideRepository
import com.apkupdater.repository.FdroidRepository
import com.apkupdater.repository.GitHubRepository
import com.apkupdater.repository.GitLabRepository
import com.apkupdater.repository.CodeBergRepository
import com.apkupdater.repository.SearchRepository
import com.apkupdater.repository.UpdatesRepository
import com.apkupdater.service.ApkMirrorService
Expand All @@ -21,6 +22,7 @@ import com.apkupdater.service.AptoideService
import com.apkupdater.service.FdroidService
import com.apkupdater.service.GitHubService
import com.apkupdater.service.GitLabService
import com.apkupdater.service.CodeBergService
import com.apkupdater.util.Clipboard
import com.apkupdater.util.Downloader
import com.apkupdater.util.SessionInstaller
Expand Down Expand Up @@ -93,6 +95,15 @@ val mainModule = module {
.create(GitLabService::class.java)
}

single {
Retrofit.Builder()
.client(get())
.baseUrl("https://codeberg.org/api/v1/")
.addConverterFactory(GsonConverterFactory.create(get()))
.build()
.create(CodeBergService::class.java)
}

single {
Retrofit.Builder()
.client(get())
Expand Down Expand Up @@ -139,6 +150,8 @@ val mainModule = module {

single { GitHubRepository(get(), get()) }

single { CodeBergRepository(get(), get()) }

single { GitLabRepository(get(), get()) }

single { ApkPureRepository(get(), get(), get()) }
Expand Down
1 change: 1 addition & 0 deletions app/src/main/kotlin/com/apkupdater/prefs/Prefs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ class Prefs(
val useApkMirror = boolean("useApkMirror", defValue = !isAndroidTv, backed = true)
val useGitHub = boolean("useGitHub", defValue = true, backed = true)
val useGitLab = boolean("useGitLab", defValue = true, backed = true)
val useCodeBerg = boolean("useCodeBerg", defValue = true, backed = true)
val useFdroid = boolean("useFdroid", defValue = true, backed = true)
val useIzzy = boolean("useIzzy", defValue = true, backed = true)
val useAptoide = boolean("useAptoide", defValue = true, backed = true)
Expand Down
178 changes: 178 additions & 0 deletions app/src/main/kotlin/com/apkupdater/repository/CodeBergRepository.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
package com.apkupdater.repository

import android.net.Uri
import android.os.Build
import android.util.Log
import io.github.g00fy2.versioncompare.Version
import com.apkupdater.BuildConfig
import com.apkupdater.data.codeberg.CodeBergApps
import com.apkupdater.data.codeberg.CodeBergRelease
import com.apkupdater.data.codeberg.CodeBergReleaseAsset
import com.apkupdater.data.ui.AppInstalled
import com.apkupdater.data.ui.AppUpdate
import com.apkupdater.data.ui.CodeBergSource
import com.apkupdater.data.ui.getApp
import com.apkupdater.prefs.Prefs
import com.apkupdater.service.CodeBergService
import com.apkupdater.util.combine
import com.apkupdater.util.filterVersionTag
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import java.util.Scanner

class CodeBergRepository(
private val service: CodeBergService,
private val prefs: Prefs
) {

suspend fun updates(apps: List<AppInstalled>) = flow {
val checks = mutableListOf<Flow<List<AppUpdate>>>()

CodeBergApps.forEachIndexed { i, app ->
if (i != 0) {
apps.find { it.packageName == app.packageName }?.let {
checks.add(checkApp(apps, app.user, app.repo, app.packageName, it.version, app.extra))
}
}
}

checks.combine { all ->
emit(all.flatMap { it })
}.collect()
}.catch {
emit(emptyList())
Log.e("CodeBergRepository", "Error fetching releases.", it)
}

suspend fun search(text: String) = flow {
val checks = mutableListOf<Flow<List<AppUpdate>>>()

CodeBergApps.forEach { app ->
if (app.repo.contains(text, true) || app.user.contains(text, true) || app.packageName.contains(text, true)) {
checks.add(checkApp(null, app.user, app.repo, app.packageName, "?", null))
}
}

if (checks.isEmpty()) {
emit(Result.success(emptyList()))
} else {
checks.combine { all ->
val r = all.flatMap { it }
emit(Result.success(r))
}.collect()
}
}.catch {
emit(Result.failure(it))
Log.e("CodeBergRepository", "Error searching.", it)
}

private fun checkApp(
apps: List<AppInstalled>?,
user: String,
repo: String,
packageName: String,
currentVersion: String,
extra: Regex?
) = flow {
val releases = service.getReleases(user, repo)
.filter { filterPreRelease(it) }
.filter { findApkAsset(it.assets).isNotEmpty() }

if (releases.isNotEmpty() && Version(filterVersionTag(releases[0].tag_name)) > Version(currentVersion)) {
val app = apps?.getApp(packageName)
emit(listOf(AppUpdate(
name = repo,
packageName = packageName,
version = releases[0].tag_name,
oldVersion = app?.version ?: "?",
versionCode = 0L,
oldVersionCode = app?.versionCode ?: 0L,
source = CodeBergSource,
link = findApkAssetArch(releases[0].assets, extra),
whatsNew = releases[0].body,
iconUri = if (apps == null) Uri.parse(releases[0].author.avatar_url) else Uri.EMPTY
)))
} else {
emit(emptyList())
}
}.catch {
emit(emptyList())
Log.e("CodeBergRepository", "Error fetching releases for $packageName.", it)
}

private fun getVersions(name: String) = runCatching {
val scanner = Scanner(name)
val version = scanner.next()
val versionCode = scanner.next().trim('(', ')').toLong()
Pair(version, versionCode)
}.getOrDefault(Pair(name, 0L))

private fun filterPreRelease(release: CodeBergRelease) = when {
prefs.ignorePreRelease.get() && release.prerelease -> false
else -> true
}

private fun findApkAsset(assets: List<CodeBergReleaseAsset>) = assets
.filter { it.browser_download_url.endsWith(".apk", true) }
.maxByOrNull { it.size }
?.browser_download_url
.orEmpty()

private fun findApkAssetArch(
assets: List<CodeBergReleaseAsset>,
extra: Regex?
): String {
val apks = assets
.filter { it.browser_download_url.endsWith(".apk", true) }
.filter { filterExtra(it, extra) }

when {
apks.isEmpty() -> return ""
apks.size == 1 -> return apks.first().browser_download_url
else -> {
// Try to match exact arch
Build.SUPPORTED_ABIS.forEach { arch ->
apks.forEach { apk ->
if (apk.browser_download_url.contains(arch, true)) {
return apk.browser_download_url
}
}
}
// Try to match arm64
if (Build.SUPPORTED_ABIS.contains("arm64-v8a")) {
apks.forEach { apk ->
if (apk.browser_download_url.contains("arm64", true)) {
return apk.browser_download_url
}
}
}
// Try to match x64
if (Build.SUPPORTED_ABIS.contains("x86_64")) {
apks.forEach { apk ->
if (apk.browser_download_url.contains("x64", true)) {
return apk.browser_download_url
}
}
}
// Try to match arm
if (Build.SUPPORTED_ABIS.contains("armeabi-v7a")) {
apks.forEach { apk ->
if (apk.browser_download_url.contains("arm", true)) {
return apk.browser_download_url
}
}
}
// If no match, return biggest apk in the hope it's universal
return apks.maxByOrNull { it.size }?.browser_download_url.orEmpty()
}
}
}

private fun filterExtra(asset: CodeBergReleaseAsset, extra: Regex?) = when(extra) {
null -> true
else -> asset.browser_download_url.matches(extra)
}

}
15 changes: 15 additions & 0 deletions app/src/main/kotlin/com/apkupdater/service/CodeBergService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.apkupdater.service

import com.apkupdater.data.codeberg.CodeBergRelease
import retrofit2.http.GET
import retrofit2.http.Path

interface CodeBergService {

@GET("/repos/{user}/{repo}/releases")
suspend fun getReleases(
@Path("user") user: String = "rumboalla",
Jvr2022 marked this conversation as resolved.
Show resolved Hide resolved
@Path("repo") repo: String = "apkupdater"
): List<CodeBergRelease>

}
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,12 @@ fun Settings(viewModel: SettingsViewModel) = LazyColumn {
stringResource(R.string.source_aptoide),
R.drawable.ic_aptoide
)
SwitchSetting(
{ viewModel.getUseCodeBerg() },
{ viewModel.setUseCodeBerg(it) },
stringResource(R.string.source_codeberg),
R.drawable.ic_codeberg
)
SwitchSetting(
{ viewModel.getUseApkPure() },
{ viewModel.setUseApkPure(it) },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ class SettingsViewModel(
fun setUseIzzy(b: Boolean) = prefs.useIzzy.put(b)
fun getUseGitHub() = prefs.useGitHub.get()
fun setUseGitHub(b: Boolean) = prefs.useGitHub.put(b)
fun getUseCodeBerg() = prefs.useCodeBerg.get()
fun setUseCodeBerg(b: Boolean) = prefs.useCodeBerg.put(b)
fun getUseGitLab() = prefs.useGitLab.get()
fun setUseGitLab(b: Boolean) = prefs.useGitLab.put(b)
fun getUseAptoide() = prefs.useAptoide.get()
Expand Down
Binary file added app/src/main/res/drawable/ic_codeberg.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
<string name="source_github" translatable="false">GitHub</string>
<string name="source_gitlab" translatable="false">GitLab</string>
<string name="source_apkpure" translatable="false">APKPure</string>
<string name="source_codeberg" translatable="false">CodeBerg</string>
<string name="about">About</string>
<string name="frequency">Frequency</string>
<string name="theme">Theme</string>
Expand All @@ -69,4 +70,4 @@
<item quantity="other">Found %1$d updates.</item>
</plurals>

</resources>
</resources>