mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-10 09:07:47 +01:00
feat: get bundle information from jar manifest (#2027)
This commit is contained in:
parent
a12c5c583b
commit
1ce56af3b1
17 changed files with 162 additions and 100 deletions
|
@ -9,7 +9,7 @@ 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?)
|
||||
|
@ -17,6 +17,9 @@ interface PatchBundleDao {
|
|||
@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()
|
||||
|
||||
|
|
|
@ -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 ->
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,12 +1,20 @@
|
|||
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
|
||||
|
||||
|
@ -14,13 +22,21 @@ 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.
|
||||
*/
|
||||
|
@ -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 {
|
||||
|
@ -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(
|
||||
""
|
||||
)
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
|
|
@ -22,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(),
|
||||
|
@ -36,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 = "Default",
|
||||
name = "",
|
||||
versionInfo = VersionInfo(),
|
||||
source = Source.API,
|
||||
autoUpdate = false
|
||||
|
|
|
@ -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?) {
|
||||
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) {
|
||||
val entity = persistenceRepo.create("", SourceInfo.from(url), autoUpdate)
|
||||
addBundle(entity.load())
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<*>> {
|
||||
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -30,7 +30,7 @@ import app.revanced.manager.util.isDebuggable
|
|||
fun BaseBundleDialog(
|
||||
modifier: Modifier = Modifier,
|
||||
isDefault: Boolean,
|
||||
name: String,
|
||||
name: String?,
|
||||
onNameChange: ((String) -> Unit)? = null,
|
||||
remoteUrl: String?,
|
||||
onRemoteUrlChange: ((String) -> Unit)? = null,
|
||||
|
@ -52,6 +52,7 @@ fun BaseBundleDialog(
|
|||
)
|
||||
.then(modifier)
|
||||
) {
|
||||
if (name != null) {
|
||||
var showNameInputDialog by rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
|
@ -78,6 +79,7 @@ fun BaseBundleDialog(
|
|||
showNameInputDialog = true
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
remoteUrl?.takeUnless { isDefault }?.let { url ->
|
||||
var showUrlInputDialog by rememberSaveable {
|
||||
|
|
|
@ -23,9 +23,9 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
|||
import app.revanced.manager.R
|
||||
import app.revanced.manager.domain.bundles.LocalPatchBundle
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.asRemoteOrNull
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.isDefault
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.propsOrNullFlow
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.isDefault
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.nameState
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
@ -44,7 +44,7 @@ fun BundleInformationDialog(
|
|||
bundle.state.map { it.patchBundleOrNull()?.patches?.size ?: 0 }
|
||||
}.collectAsStateWithLifecycle(0)
|
||||
val props by remember(bundle) {
|
||||
bundle.propsOrNullFlow()
|
||||
bundle.propsFlow()
|
||||
}.collectAsStateWithLifecycle(null)
|
||||
|
||||
if (viewCurrentBundlePatches) {
|
||||
|
@ -63,10 +63,12 @@ fun BundleInformationDialog(
|
|||
dismissOnBackPress = true
|
||||
)
|
||||
) {
|
||||
val bundleName by bundle.nameState
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
BundleTopBar(
|
||||
title = bundle.name,
|
||||
title = bundleName,
|
||||
onBackClick = onDismissRequest,
|
||||
onBackIcon = {
|
||||
Icon(
|
||||
|
@ -98,7 +100,8 @@ fun BundleInformationDialog(
|
|||
BaseBundleDialog(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
isDefault = bundle.isDefault,
|
||||
name = bundle.name,
|
||||
name = bundleName,
|
||||
onNameChange = { composableScope.launch { bundle.setName(it) } },
|
||||
remoteUrl = bundle.asRemoteOrNull?.endpoint,
|
||||
patchCount = patchCount,
|
||||
version = props?.versionInfo?.patches,
|
||||
|
|
|
@ -27,7 +27,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.propsOrNullFlow
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.nameState
|
||||
import kotlinx.coroutines.flow.map
|
||||
|
||||
@OptIn(ExperimentalFoundationApi::class)
|
||||
|
@ -45,8 +45,9 @@ fun BundleItem(
|
|||
val state by bundle.state.collectAsStateWithLifecycle()
|
||||
|
||||
val version by remember(bundle) {
|
||||
bundle.propsOrNullFlow().map { props -> props?.versionInfo?.patches }
|
||||
bundle.propsFlow().map { props -> props?.versionInfo?.patches }
|
||||
}.collectAsStateWithLifecycle(null)
|
||||
val name by bundle.nameState
|
||||
|
||||
if (viewBundleDialogPage) {
|
||||
BundleInformationDialog(
|
||||
|
@ -77,10 +78,10 @@ fun BundleItem(
|
|||
}
|
||||
} else null,
|
||||
|
||||
headlineContent = { Text(text = bundle.name) },
|
||||
headlineContent = { Text(name) },
|
||||
supportingContent = {
|
||||
state.patchBundleOrNull()?.patches?.size?.let { patchCount ->
|
||||
Text(text = pluralStringResource(R.plurals.patch_count, patchCount, patchCount))
|
||||
Text(pluralStringResource(R.plurals.patch_count, patchCount, patchCount))
|
||||
}
|
||||
},
|
||||
trailingContent = {
|
||||
|
@ -95,7 +96,7 @@ fun BundleItem(
|
|||
|
||||
icon?.let { (vector, description) ->
|
||||
Icon(
|
||||
imageVector = vector,
|
||||
vector,
|
||||
contentDescription = stringResource(description),
|
||||
modifier = Modifier.size(24.dp),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
|
|
|
@ -12,10 +12,12 @@ import androidx.compose.material3.ModalBottomSheet
|
|||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.nameState
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
|
@ -51,6 +53,7 @@ fun BundleSelector(bundles: List<PatchBundleSource>, onFinish: (PatchBundleSourc
|
|||
)
|
||||
}
|
||||
bundles.forEach {
|
||||
val name by it.nameState
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
|
@ -62,7 +65,7 @@ fun BundleSelector(bundles: List<PatchBundleSource>, onFinish: (PatchBundleSourc
|
|||
}
|
||||
) {
|
||||
Text(
|
||||
text = it.name,
|
||||
name,
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface
|
||||
)
|
||||
|
|
|
@ -35,11 +35,10 @@ import app.revanced.manager.util.JAR_MIMETYPE
|
|||
@Composable
|
||||
fun ImportBundleDialog(
|
||||
onDismissRequest: () -> Unit,
|
||||
onRemoteSubmit: (String, String, Boolean) -> Unit,
|
||||
onLocalSubmit: (String, Uri, Uri?) -> Unit,
|
||||
onRemoteSubmit: (String, Boolean) -> Unit,
|
||||
onLocalSubmit: (Uri, Uri?) -> Unit,
|
||||
initialBundleType: BundleType
|
||||
) {
|
||||
var name by rememberSaveable { mutableStateOf("") }
|
||||
var remoteUrl by rememberSaveable { mutableStateOf("") }
|
||||
var autoUpdate by rememberSaveable { mutableStateOf(true) }
|
||||
var bundleType by rememberSaveable { mutableStateOf(initialBundleType) }
|
||||
|
@ -48,7 +47,7 @@ fun ImportBundleDialog(
|
|||
|
||||
val inputsAreValid by remember {
|
||||
derivedStateOf {
|
||||
name.isNotEmpty() && if (bundleType == BundleType.Local) patchBundle != null else remoteUrl.isNotEmpty()
|
||||
if (bundleType == BundleType.Local) patchBundle != null else remoteUrl.isNotEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -56,6 +55,7 @@ fun ImportBundleDialog(
|
|||
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
uri?.let { patchBundle = it }
|
||||
}
|
||||
|
||||
fun launchPatchActivity() {
|
||||
patchActivityLauncher.launch(JAR_MIMETYPE)
|
||||
}
|
||||
|
@ -64,6 +64,7 @@ fun ImportBundleDialog(
|
|||
rememberLauncherForActivityResult(ActivityResultContracts.GetContent()) { uri ->
|
||||
uri?.let { integrations = it }
|
||||
}
|
||||
|
||||
fun launchIntegrationsActivity() {
|
||||
integrationsActivityLauncher.launch(APK_MIMETYPE)
|
||||
}
|
||||
|
@ -91,12 +92,8 @@ fun ImportBundleDialog(
|
|||
enabled = inputsAreValid,
|
||||
onClick = {
|
||||
when (bundleType) {
|
||||
BundleType.Local -> onLocalSubmit(
|
||||
name,
|
||||
patchBundle!!,
|
||||
integrations
|
||||
)
|
||||
BundleType.Remote -> onRemoteSubmit(name, remoteUrl, autoUpdate)
|
||||
BundleType.Local -> onLocalSubmit(patchBundle!!, integrations)
|
||||
BundleType.Remote -> onRemoteSubmit(remoteUrl, autoUpdate)
|
||||
}
|
||||
},
|
||||
modifier = Modifier.padding(end = 16.dp)
|
||||
|
@ -110,8 +107,7 @@ fun ImportBundleDialog(
|
|||
BaseBundleDialog(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
isDefault = false,
|
||||
name = name,
|
||||
onNameChange = { name = it },
|
||||
name = null,
|
||||
remoteUrl = remoteUrl.takeUnless { bundleType == BundleType.Local },
|
||||
onRemoteUrlChange = { remoteUrl = it },
|
||||
patchCount = 0,
|
||||
|
|
|
@ -75,7 +75,7 @@ data class BundleInfo(
|
|||
targetList.add(it)
|
||||
}
|
||||
|
||||
BundleInfo(source.name, source.uid, supported, unsupported, universal)
|
||||
BundleInfo(source.getName(), source.uid, supported, unsupported, universal)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ import androidx.compose.ui.unit.dp
|
|||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.data.room.apps.installed.InstalledApp
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.isDefault
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.isDefault
|
||||
import app.revanced.manager.patcher.aapt.Aapt
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.AutoUpdatesDialog
|
||||
|
@ -102,13 +102,13 @@ fun DashboardScreen(
|
|||
|
||||
ImportBundleDialog(
|
||||
onDismissRequest = ::dismiss,
|
||||
onLocalSubmit = { name, patches, integrations ->
|
||||
onLocalSubmit = { patches, integrations ->
|
||||
dismiss()
|
||||
vm.createLocalSource(name, patches, integrations)
|
||||
vm.createLocalSource(patches, integrations)
|
||||
},
|
||||
onRemoteSubmit = { name, url, autoUpdate ->
|
||||
onRemoteSubmit = { url, autoUpdate ->
|
||||
dismiss()
|
||||
vm.createRemoteSource(name, url, autoUpdate)
|
||||
vm.createRemoteSource(url, autoUpdate)
|
||||
},
|
||||
initialBundleType = it
|
||||
)
|
||||
|
|
|
@ -12,7 +12,7 @@ import androidx.lifecycle.viewModelScope
|
|||
import app.revanced.manager.R
|
||||
import app.revanced.manager.data.platform.NetworkInfo
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.asRemoteOrNull
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
||||
import app.revanced.manager.domain.bundles.RemotePatchBundle
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||
|
@ -80,20 +80,17 @@ class DashboardViewModel(
|
|||
fun cancelSourceSelection() {
|
||||
selectedSources.clear()
|
||||
}
|
||||
fun createLocalSource(name: String, patchBundle: Uri, integrations: Uri?) =
|
||||
fun createLocalSource(patchBundle: Uri, integrations: Uri?) =
|
||||
viewModelScope.launch {
|
||||
contentResolver.openInputStream(patchBundle)!!.use { patchesStream ->
|
||||
val integrationsStream = integrations?.let { contentResolver.openInputStream(it) }
|
||||
try {
|
||||
patchBundleRepository.createLocal(name, patchesStream, integrationsStream)
|
||||
} finally {
|
||||
integrationsStream?.close()
|
||||
integrations?.let { contentResolver.openInputStream(it) }.use { integrationsStream ->
|
||||
patchBundleRepository.createLocal(patchesStream, integrationsStream)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createRemoteSource(name: String, apiUrl: String, autoUpdate: Boolean) =
|
||||
viewModelScope.launch { patchBundleRepository.createRemote(name, apiUrl, autoUpdate) }
|
||||
fun createRemoteSource(apiUrl: String, autoUpdate: Boolean) =
|
||||
viewModelScope.launch { patchBundleRepository.createRemote(apiUrl, autoUpdate) }
|
||||
|
||||
fun delete(bundle: PatchBundleSource) =
|
||||
viewModelScope.launch { patchBundleRepository.remove(bundle) }
|
||||
|
@ -107,9 +104,9 @@ class DashboardViewModel(
|
|||
RemotePatchBundle.updateFailMsg
|
||||
) {
|
||||
if (bundle.update())
|
||||
app.toast(app.getString(R.string.bundle_update_success, bundle.name))
|
||||
app.toast(app.getString(R.string.bundle_update_success, bundle.getName()))
|
||||
else
|
||||
app.toast(app.getString(R.string.bundle_update_unavailable, bundle.name))
|
||||
app.toast(app.getString(R.string.bundle_update_unavailable, bundle.getName()))
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,7 +11,7 @@ import androidx.activity.result.contract.ActivityResultContracts
|
|||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Companion.asRemoteOrNull
|
||||
import app.revanced.manager.domain.bundles.PatchBundleSource.Extensions.asRemoteOrNull
|
||||
import app.revanced.manager.domain.manager.KeystoreManager
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.domain.repository.PatchBundleRepository
|
||||
|
|
|
@ -25,6 +25,8 @@
|
|||
|
||||
<string name="bundle_missing">Missing</string>
|
||||
<string name="bundle_error">Error</string>
|
||||
<string name="bundle_name_default">Default</string>
|
||||
<string name="bundle_name_fallback">Unnamed</string>
|
||||
|
||||
<string name="selected_app_meta">%1$s • %2$d available patches</string>
|
||||
|
||||
|
|
Loading…
Reference in a new issue