feat: custom sources

This commit is contained in:
Canny 2022-10-12 16:19:21 +03:00 committed by GitHub
parent 69df08e0ed
commit 03fea253f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 247 additions and 143 deletions

View File

@ -1,81 +0,0 @@
package app.revanced.manager.api
import android.util.Log
import app.revanced.manager.dto.github.Assets
import app.revanced.manager.preferences.PreferencesManager
import app.revanced.manager.repository.GitHubRepository
import com.vk.knet.core.Knet
import com.vk.knet.core.http.HttpMethod
import com.vk.knet.core.http.HttpRequest
import com.vk.knet.cornet.CronetKnetEngine
import kotlinx.coroutines.*
import java.io.File
class API(private val repository: GitHubRepository, private val prefs: PreferencesManager, cronet: CronetKnetEngine) {
val client = Knet.Build(cronet)
suspend fun findAsset(repo: String, file: String): PatchesAsset {
val asset = repository.fetchAssets().tools.findAsset(repo, file) ?: throw MissingAssetException()
return PatchesAsset(asset)
}
private fun List<Assets>.findAsset(repo: String, file: String) = find { asset ->
(asset.name.contains(file) && asset.repository.contains(repo))
}
suspend fun downloadPatchBundle(workdir: File): File {
return try {
withContext(Dispatchers.IO) {
val (_, out) = downloadAsset(
workdir,
findAsset(prefs.srcPatches.toString(), ".jar")
)
out
}
} catch (e: Exception) {
throw Exception("Failed to download patch bundle", e)
}
}
suspend fun downloadIntegrations(workdir: File): File {
return try {
val (_, out) = downloadAsset(
workdir,
findAsset(prefs.srcIntegrations.toString(), ".apk")
)
out
} catch (e: Exception) {
throw Exception("Failed to download integrations", e)
}
}
fun downloadAsset(
workdir: File,
assets: PatchesAsset
): Pair<PatchesAsset, File> {
val out = workdir.resolve("${assets.asset.version}-${assets.asset.name}")
if (out.exists()) {
Log.d(
"ReVanced Manager",
"Skipping downloading asset ${assets.asset.name} because it exists in cache!"
)
return assets to out
}
Log.d("ReVanced Manager", "Downloading asset ${assets.asset.name}")
val file = client.execute(
HttpRequest(
HttpMethod.GET,
assets.asset.downloadUrl
)
).body!!.asBytes()
out.writeBytes(file)
return assets to out
}
}
data class PatchesAsset(
val asset: Assets
)
class MissingAssetException : Exception()

View File

@ -0,0 +1,74 @@
package app.revanced.manager.api
import android.util.Log
import app.revanced.manager.dto.github.Release
import app.revanced.manager.util.tag
import com.vk.knet.core.Knet
import com.vk.knet.core.http.HttpMethod
import com.vk.knet.core.http.HttpRequest
import com.vk.knet.cornet.CronetKnetEngine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.io.File
class GitHubAPI(cronet: CronetKnetEngine, val json: Json) {
private val client = Knet.Build(cronet)
private suspend fun findAsset(repo: String, file: String): PatchesAsset {
val release = getLatestRelease(repo)
val asset = release.assets.findAsset(file) ?: throw MissingAssetException()
return PatchesAsset(release, asset)
}
private fun List<Release.Asset>.findAsset(file: String) = find { asset ->
(asset.name.contains(file) && !asset.name.contains("-sources") && !asset.name.contains("-javadoc"))
}
suspend fun downloadAsset(
workdir: File,
repo: String,
name: String
): File {
val asset = findAsset(repo, name).asset
val out = workdir.resolve(asset.name)
if (out.exists()) {
Log.d(
tag,
"Skipping downloading asset ${asset.name} because it exists in cache!"
)
return out
}
Log.d(tag, "Downloading asset ${asset.name} from GitHub API.")
val file = client.execute(
HttpRequest(
HttpMethod.GET,
asset.downloadUrl
)
).body!!.asBytes()
out.writeBytes(file)
return out
}
private suspend fun getLatestRelease(repo: String) = withContext(Dispatchers.IO) {
val stream = client.execute(
HttpRequest(
HttpMethod.GET,
"$baseUrl/$repo/releases"
)
).body!!.asString()
val res: List<Release> = json.decodeFromString(stream)
res.first()
}
data class PatchesAsset(
val release: Release,
val asset: Release.Asset
)
private companion object {
private const val baseUrl = "https://api.github.com/repos"
}
}

View File

@ -0,0 +1,88 @@
package app.revanced.manager.api
import android.util.Log
import app.revanced.manager.dto.revanced.Assets
import app.revanced.manager.dto.revanced.Repositories
import app.revanced.manager.dto.revanced.Tools
import app.revanced.manager.util.tag
import com.vk.knet.core.Knet
import com.vk.knet.core.http.HttpMethod
import com.vk.knet.core.http.HttpRequest
import com.vk.knet.cornet.CronetKnetEngine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import java.io.File
class ReVancedAPI(cronet: CronetKnetEngine, val json: Json) {
val client = Knet.Build(cronet)
suspend fun ping(): Boolean {
return try {
withContext(Dispatchers.Default) {
client.execute(HttpRequest(HttpMethod.GET, "$apiUrl/")).statusCode == 200
}
} catch (e: Exception) {
Log.e(tag, "ReVanced API isn't available at the moment. switching to GitHub API")
false
}
}
suspend fun fetchAssets() = withContext(Dispatchers.Default) {
val stream = client.execute(HttpRequest(HttpMethod.GET, "$apiUrl/tools")).body!!.asString()
json.decodeFromString(stream) as Tools
}
suspend fun fetchContributors() = withContext(Dispatchers.Default) {
val stream =
client.execute(HttpRequest(HttpMethod.GET, "$apiUrl/contributors")).body!!.asString()
json.decodeFromString(stream) as Repositories
}
suspend fun findAsset(repo: String, file: String): PatchesAsset {
val asset = fetchAssets().tools.findAsset(repo, file) ?: throw MissingAssetException()
return PatchesAsset(asset)
}
private fun List<Assets>.findAsset(repo: String, file: String) = find { asset ->
(asset.name.contains(file) && asset.repository.contains(repo))
}
suspend fun downloadAsset(
workdir: File,
repo: String,
name: String,
): File {
val asset = findAsset(repo, name).asset
val out = workdir.resolve(asset.name)
if (out.exists()) {
Log.d(
tag,
"Skipping downloading asset ${asset.name} because it exists in cache!"
)
return out
}
Log.d(tag, "Downloading asset ${asset.name} from ReVanced API.")
val file = client.execute(
HttpRequest(
HttpMethod.GET, asset.downloadUrl
)
).body!!.asBytes()
out.writeBytes(file)
return out
}
private companion object {
private const val apiUrl = "https://releases.rvcd.win"
}
}
data class PatchesAsset(
val asset: Assets
)
class MissingAssetException : Exception()

View File

@ -1,11 +1,11 @@
package app.revanced.manager.di
import app.revanced.manager.api.API
import app.revanced.manager.repository.GitHubRepository
import app.revanced.manager.api.GitHubAPI
import app.revanced.manager.api.ReVancedAPI
import org.koin.core.module.dsl.singleOf
import org.koin.dsl.module
val repositoryModule = module {
singleOf(::GitHubRepository)
singleOf(::API)
singleOf(::GitHubAPI)
singleOf(::ReVancedAPI)
}

View File

@ -6,5 +6,5 @@ import org.koin.androidx.workmanager.dsl.worker
import org.koin.dsl.module
val workerModule = module {
worker { PatcherWorker( androidContext(), get(), get())}
worker { PatcherWorker(androidContext(), get(), get(), get(), get()) }
}

View File

@ -0,0 +1,15 @@
package app.revanced.manager.dto.github
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
class Release(
val assets: List<Asset>,
) {
@Serializable
data class Asset(
@SerialName("browser_download_url") val downloadUrl: String,
@SerialName("name") val name: String
)
}

View File

@ -1,4 +1,4 @@
package app.revanced.manager.dto.github
package app.revanced.manager.dto.revanced
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -1,4 +1,4 @@
package app.revanced.manager.dto.github
package app.revanced.manager.dto.revanced
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

View File

@ -18,14 +18,17 @@ import app.revanced.manager.R
import app.revanced.manager.Variables.patches
import app.revanced.manager.Variables.selectedAppPackage
import app.revanced.manager.Variables.selectedPatches
import app.revanced.manager.api.API
import app.revanced.manager.api.GitHubAPI
import app.revanced.manager.api.ReVancedAPI
import app.revanced.manager.patcher.aapt.Aapt
import app.revanced.manager.patcher.aligning.ZipAligner
import app.revanced.manager.patcher.aligning.zip.ZipFile
import app.revanced.manager.patcher.aligning.zip.structures.ZipEntry
import app.revanced.manager.patcher.signing.Signer
import app.revanced.manager.preferences.PreferencesManager
import app.revanced.manager.ui.Resource
import app.revanced.manager.ui.viewmodel.Logging
import app.revanced.manager.util.ghIntegrations
import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.extensions.PatchExtensions.patchName
@ -38,8 +41,15 @@ import java.io.File
import java.io.FileNotFoundException
import java.nio.file.Files
import java.nio.file.StandardCopyOption
import java.time.LocalDateTime
class PatcherWorker(context: Context, parameters: WorkerParameters, private val api: API) :
class PatcherWorker(
context: Context,
parameters: WorkerParameters,
private val reVancedAPI: ReVancedAPI,
private val gitHubAPI: GitHubAPI,
private val prefs: PreferencesManager
) :
CoroutineWorker(context, parameters), KoinComponent {
val tag = "ReVanced Manager"
private val workdir = createWorkDir()
@ -122,8 +132,16 @@ class PatcherWorker(context: Context, parameters: WorkerParameters, private val
val outputFile = File(applicationContext.filesDir, "output.apk")
val cacheDirectory = workdir.resolve("cache")
Logging.log += "Downloading integrations\n"
val integrations = api.downloadIntegrations(integrationsCacheDir)
val integrations = if (prefs.srcIntegrations != ghIntegrations || !reVancedAPI.ping()) {
Logging.log += "Downloading integrations from GitHub API\n"
gitHubAPI.downloadAsset(integrationsCacheDir, prefs.srcIntegrations!!, ".apk")
} else {
Logging.log += "Downloading integrations from ReVanced API\n"
reVancedAPI.downloadAsset(
integrationsCacheDir,
prefs.srcIntegrations!!, ".apk"
)
}
Logging.log += "Copying base.apk from device\n"
withContext(Dispatchers.IO) {
@ -181,7 +199,7 @@ class PatcherWorker(context: Context, parameters: WorkerParameters, private val
Logging.log += "Saving file\n"
val result = patcher.save() // compile apk
ZipFile(patchedFile).use { fs -> // somehow this function is the most resource intensive
ZipFile(patchedFile).use { fs ->
result.dexFiles.forEach {
Logging.log += "Writing dex file ${it.name}\n"
fs.addEntryCompressData(ZipEntry.createWithName(it.name), it.stream.readBytes())
@ -200,7 +218,8 @@ class PatcherWorker(context: Context, parameters: WorkerParameters, private val
withContext(Dispatchers.IO) {
Files.copy(
outputFile.inputStream(),
reVancedFolder.resolve(appInfo.packageName + ".apk").toPath(),
reVancedFolder.resolve(appInfo.packageName + "-" + LocalDateTime.now() + ".apk")
.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}

View File

@ -1,31 +0,0 @@
package app.revanced.manager.repository
import app.revanced.manager.dto.github.Repositories
import app.revanced.manager.dto.github.Tools
import com.vk.knet.core.Knet
import com.vk.knet.core.http.HttpMethod
import com.vk.knet.core.http.HttpRequest
import com.vk.knet.cornet.CronetKnetEngine
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
class GitHubRepository(cronet: CronetKnetEngine, private val json: Json) {
private val client = Knet.Build(cronet)
suspend fun fetchAssets() = withContext(Dispatchers.Default) {
val stream = client.execute(HttpRequest(HttpMethod.GET, "$apiUrl/tools")).body!!.asString()
json.decodeFromString(stream) as Tools
}
suspend fun fetchContributors() = withContext(Dispatchers.Default) {
val stream = client.execute(HttpRequest(HttpMethod.GET,"$apiUrl/contributors")).body!!.asString()
json.decodeFromString(stream) as Repositories
}
private companion object {
private const val apiUrl = "https://releases.rvcd.win"
}
}

View File

@ -15,7 +15,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.dto.github.Contributor
import app.revanced.manager.dto.revanced.Contributor
import app.revanced.manager.ui.viewmodel.ContributorsViewModel
import coil.compose.AsyncImage
import com.google.accompanist.flowlayout.FlowRow
@ -62,7 +62,13 @@ fun ContributorsCard(
.combinedClickable(
onClick = { vm.openUserProfile(contributor.username) },
onLongClick = {
Toast.makeText(context, contributor.username, Toast.LENGTH_SHORT).show()
Toast
.makeText(
context,
contributor.username,
Toast.LENGTH_SHORT
)
.show()
}
)
)

View File

@ -131,12 +131,14 @@ fun PatchesSelectorSubscreen(
}
}
}
}
else {
Column(Modifier.fillMaxSize(), Arrangement.Center, Alignment.CenterHorizontally) {
} else {
Column(
Modifier.fillMaxSize(),
Arrangement.Center,
Alignment.CenterHorizontally
) {
Text(text = "No compatible patches found.")
}
}
}
else -> LoadingIndicator(null)

View File

@ -4,14 +4,14 @@ import android.app.Application
import androidx.compose.runtime.mutableStateListOf
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.revanced.manager.dto.github.Contributor
import app.revanced.manager.repository.GitHubRepository
import app.revanced.manager.api.ReVancedAPI
import app.revanced.manager.dto.revanced.Contributor
import app.revanced.manager.util.*
import kotlinx.coroutines.launch
class ContributorsViewModel(
private val app: Application,
private val repository: GitHubRepository
private val reVancedAPI: ReVancedAPI
) : ViewModel() {
val patcherContributorsList = mutableStateListOf<Contributor>()
val patchesContributorsList = mutableStateListOf<Contributor>()
@ -21,7 +21,7 @@ class ContributorsViewModel(
private fun loadContributors() {
viewModelScope.launch {
val contributors = repository.fetchContributors()
val contributors = reVancedAPI.fetchContributors()
contributors.repositories.forEach { repo ->
when (repo.name) {
ghCli -> {

View File

@ -8,8 +8,8 @@ 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.api.ReVancedAPI
import app.revanced.manager.dto.revanced.Assets
import app.revanced.manager.util.ghManager
import app.revanced.manager.util.ghPatcher
import app.revanced.manager.util.tag
@ -19,7 +19,7 @@ import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat
import java.util.*
class DashboardViewModel(private val repository: GitHubRepository) : ViewModel() {
class DashboardViewModel(private val reVancedApi: ReVancedAPI) : ViewModel() {
private var _latestPatcherCommit: Assets? by mutableStateOf(null)
val patcherCommitDate: String
get() = _latestPatcherCommit?.commitDate ?: "unknown"
@ -35,7 +35,7 @@ class DashboardViewModel(private val repository: GitHubRepository) : ViewModel()
private fun fetchLastCommit() {
viewModelScope.launch {
try {
val repo = withContext(Dispatchers.Default) {repository.fetchAssets()}
val repo = withContext(Dispatchers.Default) { reVancedApi.fetchAssets() }
for (asset in repo.tools) {
when (asset.repository) {
ghPatcher -> {

View File

@ -11,8 +11,12 @@ import androidx.lifecycle.viewModelScope
import app.revanced.manager.Variables.patches
import app.revanced.manager.Variables.selectedAppPackage
import app.revanced.manager.Variables.selectedPatches
import app.revanced.manager.api.API
import app.revanced.manager.api.GitHubAPI
import app.revanced.manager.api.ReVancedAPI
import app.revanced.manager.preferences.PreferencesManager
import app.revanced.manager.ui.Resource
import app.revanced.manager.util.ghPatches
import app.revanced.manager.util.tag
import app.revanced.patcher.data.Context
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import app.revanced.patcher.extensions.PatchExtensions.options
@ -23,9 +27,13 @@ import dalvik.system.DexClassLoader
import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
class PatcherScreenViewModel(private val app: Application, private val api: API) : ViewModel() {
class PatcherScreenViewModel(
private val app: Application,
private val reVancedApi: ReVancedAPI,
private val gitHubAPI: GitHubAPI,
private val prefs: PreferencesManager
) : ViewModel() {
lateinit var patchBundleFile: String
private val tag = "ReVanced Manager"
init {
viewModelScope.launch {
@ -92,7 +100,11 @@ class PatcherScreenViewModel(private val app: Application, private val api: API)
private fun loadPatches() = viewModelScope.launch {
try {
val file = api.downloadPatchBundle(app.filesDir)
val file = if (prefs.srcPatches != ghPatches || !reVancedApi.ping()) {
gitHubAPI.downloadAsset(app.cacheDir, prefs.srcPatches!!, ".jar")
} else {
reVancedApi.downloadAsset(app.cacheDir, prefs.srcPatches!!, ".jar")
}
patchBundleFile = file.absolutePath
loadPatches0()
} catch (e: Exception) {