mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
fix: bundles not loading on Android 14
This commit is contained in:
parent
4b12ae1531
commit
18cfb56b45
@ -10,8 +10,10 @@ import java.nio.file.StandardCopyOption
|
|||||||
class LocalPatchBundle(name: String, id: Int, directory: File) : PatchBundleSource(name, id, directory) {
|
class LocalPatchBundle(name: String, id: Int, directory: File) : PatchBundleSource(name, id, directory) {
|
||||||
suspend fun replace(patches: InputStream? = null, integrations: InputStream? = null) {
|
suspend fun replace(patches: InputStream? = null, integrations: InputStream? = null) {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
patches?.let {
|
patches?.let { inputStream ->
|
||||||
Files.copy(it, patchesFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
patchBundleOutputStream().use { outputStream ->
|
||||||
|
inputStream.copyTo(outputStream)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
integrations?.let {
|
integrations?.let {
|
||||||
Files.copy(it, this@LocalPatchBundle.integrationsFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
Files.copy(it, this@LocalPatchBundle.integrationsFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
|
||||||
|
@ -6,6 +6,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.flowOf
|
import kotlinx.coroutines.flow.flowOf
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A [PatchBundle] source.
|
* A [PatchBundle] source.
|
||||||
@ -23,6 +24,16 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
|
|||||||
*/
|
*/
|
||||||
fun hasInstalled() = patchesFile.exists()
|
fun hasInstalled() = patchesFile.exists()
|
||||||
|
|
||||||
|
protected fun patchBundleOutputStream(): OutputStream = with(patchesFile) {
|
||||||
|
// Android 14+ requires dex containers to be readonly.
|
||||||
|
try {
|
||||||
|
setWritable(true, true)
|
||||||
|
outputStream()
|
||||||
|
} finally {
|
||||||
|
setReadOnly()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun load(): State {
|
private fun load(): State {
|
||||||
if (!hasInstalled()) return State.Missing
|
if (!hasInstalled()) return State.Missing
|
||||||
|
|
||||||
@ -40,7 +51,7 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
|
|||||||
sealed interface State {
|
sealed interface State {
|
||||||
fun patchBundleOrNull(): PatchBundle? = null
|
fun patchBundleOrNull(): PatchBundle? = null
|
||||||
|
|
||||||
object Missing : State
|
data object Missing : State
|
||||||
data class Failed(val throwable: Throwable) : State
|
data class Failed(val throwable: Throwable) : State
|
||||||
data class Loaded(val bundle: PatchBundle) : State {
|
data class Loaded(val bundle: PatchBundle) : State {
|
||||||
override fun patchBundleOrNull() = bundle
|
override fun patchBundleOrNull() = bundle
|
||||||
|
@ -33,16 +33,19 @@ sealed class RemotePatchBundle(name: String, id: Int, directory: File, val endpo
|
|||||||
private suspend fun download(info: BundleInfo) = withContext(Dispatchers.IO) {
|
private suspend fun download(info: BundleInfo) = withContext(Dispatchers.IO) {
|
||||||
val (patches, integrations) = info
|
val (patches, integrations) = info
|
||||||
coroutineScope {
|
coroutineScope {
|
||||||
mapOf(
|
|
||||||
patches.url to patchesFile,
|
|
||||||
integrations.url to integrationsFile
|
|
||||||
).forEach { (asset, file) ->
|
|
||||||
launch {
|
launch {
|
||||||
http.download(file) {
|
patchBundleOutputStream().use {
|
||||||
url(asset)
|
http.streamTo(it) {
|
||||||
|
url(patches.url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
launch {
|
||||||
|
http.download(integrationsFile) {
|
||||||
|
url(integrations.url)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
saveVersion(patches.version, integrations.version)
|
saveVersion(patches.version, integrations.version)
|
||||||
|
@ -18,8 +18,11 @@ import io.ktor.utils.io.ByteReadChannel
|
|||||||
import io.ktor.utils.io.core.isNotEmpty
|
import io.ktor.utils.io.core.isNotEmpty
|
||||||
import io.ktor.utils.io.core.readBytes
|
import io.ktor.utils.io.core.readBytes
|
||||||
import it.skrape.core.htmlDocument
|
import it.skrape.core.htmlDocument
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.OutputStream
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Aliucord Authors, DiamondMiner88
|
* @author Aliucord Authors, DiamondMiner88
|
||||||
@ -49,7 +52,10 @@ class HttpService(
|
|||||||
null
|
null
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.e(tag, "Failed to fetch: API error, http status: ${response.status}, body: $body")
|
Log.e(
|
||||||
|
tag,
|
||||||
|
"Failed to fetch: API error, http status: ${response.status}, body: $body"
|
||||||
|
)
|
||||||
APIResponse.Error(APIError(response.status, body))
|
APIResponse.Error(APIError(response.status, body))
|
||||||
}
|
}
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
@ -59,20 +65,19 @@ class HttpService(
|
|||||||
return response
|
return response
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun download(
|
suspend fun streamTo(
|
||||||
saveLocation: File,
|
outputStream: OutputStream,
|
||||||
builder: HttpRequestBuilder.() -> Unit
|
builder: HttpRequestBuilder.() -> Unit
|
||||||
) {
|
) {
|
||||||
http.prepareGet(builder).execute { httpResponse ->
|
http.prepareGet(builder).execute { httpResponse ->
|
||||||
if (httpResponse.status.isSuccess()) {
|
if (httpResponse.status.isSuccess()) {
|
||||||
|
|
||||||
saveLocation.outputStream().use { stream ->
|
|
||||||
val channel: ByteReadChannel = httpResponse.body()
|
val channel: ByteReadChannel = httpResponse.body()
|
||||||
|
withContext(Dispatchers.IO) {
|
||||||
while (!channel.isClosedForRead) {
|
while (!channel.isClosedForRead) {
|
||||||
val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
|
val packet = channel.readRemaining(DEFAULT_BUFFER_SIZE.toLong())
|
||||||
while (packet.isNotEmpty) {
|
while (packet.isNotEmpty) {
|
||||||
val bytes = packet.readBytes()
|
val bytes = packet.readBytes()
|
||||||
stream.write(bytes)
|
outputStream.write(bytes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -83,6 +88,11 @@ class HttpService(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun download(
|
||||||
|
saveLocation: File,
|
||||||
|
builder: HttpRequestBuilder.() -> Unit
|
||||||
|
) = saveLocation.outputStream().use { streamTo(it, builder) }
|
||||||
|
|
||||||
suspend fun getHtml(builder: HttpRequestBuilder.() -> Unit) = htmlDocument(
|
suspend fun getHtml(builder: HttpRequestBuilder.() -> Unit) = htmlDocument(
|
||||||
html = http.get(builder).bodyAsText()
|
html = http.get(builder).bodyAsText()
|
||||||
)
|
)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user