Skip to content

Commit

Permalink
Merge branch 'ReVanced:compose-dev' into compose-dev
Browse files Browse the repository at this point in the history
  • Loading branch information
indrastorms authored Jul 3, 2024
2 parents 90b657a + 48fe3a7 commit bf22b9b
Show file tree
Hide file tree
Showing 27 changed files with 273 additions and 138 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ android {
buildTypes {
debug {
applicationIdSuffix = ".debug"
resValue("string", "app_name", "ReVanced Manager Debug")
resValue("string", "app_name", "ReVanced Manager (dev)")

buildConfigField("long", "BUILD_ID", "${Random.nextLong()}L")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,17 @@ interface PatchBundleDao {
suspend fun all(): List<PatchBundleEntity>

@Query("SELECT version, integrations_version, auto_update FROM patch_bundles WHERE uid = :uid")
fun getPropsById(uid: Int): Flow<BundleProperties>
fun getPropsById(uid: Int): Flow<BundleProperties?>

@Query("UPDATE patch_bundles SET version = :patches, integrations_version = :integrations WHERE uid = :uid")
suspend fun updateVersion(uid: Int, patches: String?, integrations: String?)

@Query("UPDATE patch_bundles SET auto_update = :value WHERE uid = :uid")
suspend fun setAutoUpdate(uid: Int, value: Boolean)

@Query("UPDATE patch_bundles SET name = :value WHERE uid = :uid")
suspend fun setName(uid: Int, value: String)

@Query("DELETE FROM patch_bundles WHERE uid != 0")
suspend fun purgeCustomBundles()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import java.io.InputStream
import java.nio.file.Files
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) {
withContext(Dispatchers.IO) {
patches?.let { inputStream ->
Expand All @@ -16,10 +17,16 @@ class LocalPatchBundle(name: String, id: Int, directory: File) : PatchBundleSour
}
}
integrations?.let {
Files.copy(it, this@LocalPatchBundle.integrationsFile.toPath(), StandardCopyOption.REPLACE_EXISTING)
Files.copy(
it,
this@LocalPatchBundle.integrationsFile.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
}

reload()
reload()?.also {
saveVersion(it.readManifestAttribute("Version"), null)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,26 +1,42 @@
package app.revanced.manager.domain.bundles

import android.app.Application
import android.util.Log
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Stable
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.revanced.manager.R
import app.revanced.manager.domain.repository.PatchBundlePersistenceRepository
import app.revanced.manager.patcher.patch.PatchBundle
import app.revanced.manager.util.tag
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.map
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.File
import java.io.OutputStream

/**
* A [PatchBundle] source.
*/
@Stable
sealed class PatchBundleSource(val name: String, val uid: Int, directory: File) {
sealed class PatchBundleSource(initialName: String, val uid: Int, directory: File) : KoinComponent {
protected val configRepository: PatchBundlePersistenceRepository by inject()
private val app: Application by inject()
protected val patchesFile = directory.resolve("patches.jar")
protected val integrationsFile = directory.resolve("integrations.apk")

private val _state = MutableStateFlow(load())
val state = _state.asStateFlow()

private val _nameFlow = MutableStateFlow(initialName)
val nameFlow =
_nameFlow.map { it.ifEmpty { app.getString(if (isDefault) R.string.bundle_name_default else R.string.bundle_name_fallback) } }

suspend fun getName() = nameFlow.first()

/**
* Returns true if the bundle has been downloaded to local storage.
*/
Expand All @@ -42,13 +58,38 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
return try {
State.Loaded(PatchBundle(patchesFile, integrationsFile.takeIf(File::exists)))
} catch (t: Throwable) {
Log.e(tag, "Failed to load patch bundle $name", t)
Log.e(tag, "Failed to load patch bundle with UID $uid", t)
State.Failed(t)
}
}

fun reload() {
_state.value = load()
suspend fun reload(): PatchBundle? {
val newState = load()
_state.value = newState

val bundle = newState.patchBundleOrNull()
// Try to read the name from the patch bundle manifest if the bundle does not have a name.
if (bundle != null && _nameFlow.value.isEmpty()) {
bundle.readManifestAttribute("Name")?.let { setName(it) }
}

return bundle
}

/**
* Create a flow that emits the [app.revanced.manager.data.room.bundles.BundleProperties] of this [PatchBundleSource].
* The flow will emit null if the associated [PatchBundleSource] is deleted.
*/
fun propsFlow() = configRepository.getProps(uid)
suspend fun getProps() = configRepository.getProps(uid).first()!!

suspend fun currentVersion() = getProps().versionInfo
protected suspend fun saveVersion(patches: String?, integrations: String?) =
configRepository.updateVersion(uid, patches, integrations)

suspend fun setName(name: String) {
configRepository.setName(uid, name)
_nameFlow.value = name
}

sealed interface State {
Expand All @@ -61,9 +102,12 @@ sealed class PatchBundleSource(val name: String, val uid: Int, directory: File)
}
}

companion object {
val PatchBundleSource.isDefault get() = uid == 0
val PatchBundleSource.asRemoteOrNull get() = this as? RemotePatchBundle
fun PatchBundleSource.propsOrNullFlow() = asRemoteOrNull?.propsFlow() ?: flowOf(null)
companion object Extensions {
val PatchBundleSource.isDefault inline get() = uid == 0
val PatchBundleSource.asRemoteOrNull inline get() = this as? RemotePatchBundle
val PatchBundleSource.nameState
@Composable inline get() = nameFlow.collectAsStateWithLifecycle(
""
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package app.revanced.manager.domain.bundles

import androidx.compose.runtime.Stable
import app.revanced.manager.data.room.bundles.VersionInfo
import app.revanced.manager.domain.repository.PatchBundlePersistenceRepository
import app.revanced.manager.network.api.ReVancedAPI
import app.revanced.manager.network.api.ReVancedAPI.Extensions.findAssetByType
import app.revanced.manager.network.dto.BundleAsset
Expand All @@ -15,17 +14,14 @@ import io.ktor.client.request.url
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import org.koin.core.component.inject
import java.io.File

@Stable
sealed class RemotePatchBundle(name: String, id: Int, directory: File, val endpoint: String) :
PatchBundleSource(name, id, directory), KoinComponent {
private val configRepository: PatchBundlePersistenceRepository by inject()
PatchBundleSource(name, id, directory) {
protected val http: HttpService by inject()

protected abstract suspend fun getLatestInfo(): BundleInfo
Expand Down Expand Up @@ -70,17 +66,11 @@ sealed class RemotePatchBundle(name: String, id: Int, directory: File, val endpo
true
}

private suspend fun currentVersion() = configRepository.getProps(uid).first().versionInfo
private suspend fun saveVersion(patches: String, integrations: String) =
configRepository.updateVersion(uid, patches, integrations)

suspend fun deleteLocalFiles() = withContext(Dispatchers.Default) {
arrayOf(patchesFile, integrationsFile).forEach(File::delete)
reload()
}

fun propsFlow() = configRepository.getProps(uid)

suspend fun setAutoUpdate(value: Boolean) = configRepository.setAutoUpdate(uid, value)

companion object {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,17 @@ class KeystoreManager(app: Application, private val prefs: PreferencesManager) {
)
)
)
keystorePath.outputStream().use {
ks.store(it, null)
withContext(Dispatchers.IO) {
keystorePath.outputStream().use {
ks.store(it, null)
}
}

updatePrefs(DEFAULT, DEFAULT)
}

suspend fun import(cn: String, pass: String, keystore: InputStream): Boolean {
val keystoreData = keystore.readBytes()
val keystoreData = withContext(Dispatchers.IO) { keystore.readBytes() }

try {
val ks = ApkSigner.readKeyStore(ByteArrayInputStream(keystoreData), null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import app.revanced.manager.data.room.AppDatabase.Companion.generateUid
import app.revanced.manager.data.room.bundles.PatchBundleEntity
import app.revanced.manager.data.room.bundles.Source
import app.revanced.manager.data.room.bundles.VersionInfo
import io.ktor.http.*
import kotlinx.coroutines.flow.distinctUntilChanged

class PatchBundlePersistenceRepository(db: AppDatabase) {
Expand All @@ -23,7 +22,6 @@ class PatchBundlePersistenceRepository(db: AppDatabase) {

suspend fun reset() = dao.reset()


suspend fun create(name: String, source: Source, autoUpdate: Boolean = false) =
PatchBundleEntity(
uid = generateUid(),
Expand All @@ -37,17 +35,19 @@ class PatchBundlePersistenceRepository(db: AppDatabase) {

suspend fun delete(uid: Int) = dao.remove(uid)

suspend fun updateVersion(uid: Int, patches: String, integrations: String) =
suspend fun updateVersion(uid: Int, patches: String?, integrations: String?) =
dao.updateVersion(uid, patches, integrations)

suspend fun setAutoUpdate(uid: Int, value: Boolean) = dao.setAutoUpdate(uid, value)

suspend fun setName(uid: Int, name: String) = dao.setName(uid, name)

fun getProps(id: Int) = dao.getPropsById(id).distinctUntilChanged()

private companion object {
val defaultSource = PatchBundleEntity(
uid = 0,
name = "Main",
name = "",
versionInfo = VersionInfo(),
source = Source.API,
autoUpdate = false
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,16 +137,16 @@ class PatchBundleRepository(
private fun addBundle(patchBundle: PatchBundleSource) =
_sources.update { it.toMutableMap().apply { put(patchBundle.uid, patchBundle) } }

suspend fun createLocal(name: String, patches: InputStream, integrations: InputStream?) {
val id = persistenceRepo.create(name, SourceInfo.Local).uid
val bundle = LocalPatchBundle(name, id, directoryOf(id))
suspend fun createLocal(patches: InputStream, integrations: InputStream?) = withContext(Dispatchers.Default) {
val uid = persistenceRepo.create("", SourceInfo.Local).uid
val bundle = LocalPatchBundle("", uid, directoryOf(uid))

bundle.replace(patches, integrations)
addBundle(bundle)
}

suspend fun createRemote(name: String, url: String, autoUpdate: Boolean) {
val entity = persistenceRepo.create(name, SourceInfo.from(url), autoUpdate)
suspend fun createRemote(url: String, autoUpdate: Boolean) = withContext(Dispatchers.Default) {
val entity = persistenceRepo.create("", SourceInfo.from(url), autoUpdate)
addBundle(entity.load())
}

Expand Down Expand Up @@ -174,8 +174,8 @@ class PatchBundleRepository(

getBundlesByType<RemotePatchBundle>().forEach {
launch {
if (!it.propsFlow().first().autoUpdate) return@launch
Log.d(tag, "Updating patch bundle: ${it.name}")
if (!it.getProps().autoUpdate) return@launch
Log.d(tag, "Updating patch bundle: ${it.getName()}")
it.update()
}
}
Expand Down
9 changes: 4 additions & 5 deletions app/src/main/java/app/revanced/manager/patcher/Session.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class Session(
var nextPatchIndex = 0

updateProgress(
name = androidContext.getString(R.string.applying_patch, selectedPatches[nextPatchIndex]),
name = androidContext.getString(R.string.executing_patch, selectedPatches[nextPatchIndex]),
state = State.RUNNING
)

Expand All @@ -56,7 +56,7 @@ class Session(

if (exception != null) {
updateProgress(
name = androidContext.getString(R.string.failed_to_apply_patch, patch.name),
name = androidContext.getString(R.string.failed_to_execute_patch, patch.name),
state = State.FAILED,
message = exception.stackTraceToString()
)
Expand All @@ -72,7 +72,7 @@ class Session(

selectedPatches.getOrNull(nextPatchIndex)?.let { nextPatch ->
updateProgress(
name = androidContext.getString(R.string.applying_patch, nextPatch.name)
name = androidContext.getString(R.string.executing_patch, nextPatch.name)
)
}

Expand All @@ -82,7 +82,7 @@ class Session(
updateProgress(
state = State.COMPLETED,
name = androidContext.resources.getQuantityString(
R.plurals.patches_applied,
R.plurals.patches_executed,
selectedPatches.size,
selectedPatches.size
)
Expand All @@ -105,7 +105,6 @@ class Session(
logger.info("Merging integrations")
acceptIntegrations(integrations.toSet())
acceptPatches(selectedPatches.toSet())
updateProgress(state = State.COMPLETED) // Merging

logger.info("Applying patches...")
applyPatchesVerbose(selectedPatches.sortedBy { it.name })
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ import app.revanced.manager.util.tag
import app.revanced.patcher.PatchBundleLoader
import app.revanced.patcher.patch.Patch
import java.io.File
import java.io.IOException
import java.util.jar.JarFile

class PatchBundle(val patchesJar: File, val integrations: File?) {
private val loader = object : Iterable<Patch<*>> {
Expand All @@ -25,6 +27,17 @@ class PatchBundle(val patchesJar: File, val integrations: File?) {
*/
val patches = loader.map(::PatchInfo)

/**
* The [java.util.jar.Manifest] of [patchesJar].
*/
private val manifest = try {
JarFile(patchesJar).use { it.manifest }
} catch (_: IOException) {
null
}

fun readManifestAttribute(name: String) = manifest?.mainAttributes?.getValue(name)

/**
* Load all patches compatible with the specified package.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -184,11 +184,11 @@ class PatcherWorker(
Log.i(tag, "Patching succeeded".logFmt())
Result.success()
} catch (e: ProcessRuntime.RemoteFailureException) {
Log.e(tag, "An exception occured in the remote process while patching. ${e.originalStackTrace}".logFmt())
Log.e(tag, "An exception occurred in the remote process while patching. ${e.originalStackTrace}".logFmt())
updateProgress(state = State.FAILED, message = e.originalStackTrace)
Result.failure()
} catch (e: Exception) {
Log.e(tag, "An exception occured while patching".logFmt(), e)
Log.e(tag, "An exception occurred while patching".logFmt(), e)
updateProgress(state = State.FAILED, message = e.stackTraceToString())
Result.failure()
} finally {
Expand Down
Loading

0 comments on commit bf22b9b

Please sign in to comment.