feat: Improve bundle info screen design (#2548)

This commit is contained in:
brosssh
2025-07-08 20:23:03 +02:00
committed by oSumAtrIX
parent cd2dbcc841
commit 55524f7284
7 changed files with 93 additions and 52 deletions

View File

@@ -15,7 +15,7 @@ class LocalPatchBundle(name: String, id: Int, directory: File) :
}
reload()?.also {
saveVersionHash(it.readManifestAttribute("Version"))
saveVersionHash(it.patchBundleManifestAttributes?.version)
}
}
}

View File

@@ -38,7 +38,7 @@ sealed class PatchBundleSource(initialName: String, val uid: Int, directory: Fil
suspend fun getName() = nameFlow.first()
val versionFlow = state.map { it.patchBundleOrNull()?.readManifestAttribute("Version") }
val versionFlow = state.map { it.patchBundleOrNull()?.patchBundleManifestAttributes?.version }
val patchCountFlow = state.map { it.patchBundleOrNull()?.patches?.size ?: 0 }
/**
@@ -74,7 +74,7 @@ sealed class PatchBundleSource(initialName: String, val uid: Int, directory: Fil
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) }
bundle.patchBundleManifestAttributes?.name?.let { setName(it) }
}
return bundle

View File

@@ -8,6 +8,17 @@ import java.io.File
import java.io.IOException
import java.util.jar.JarFile
class PatchBundleManifestAttributes(
val name: String?,
val version: String?,
val description: String?,
val source: String?,
val author: String?,
val contact: String?,
val website: String?,
val license: String?
)
class PatchBundle(val patchesJar: File) {
private val loader = object : Iterable<Patch<*>> {
private fun load(): Iterable<Patch<*>> {
@@ -36,7 +47,20 @@ class PatchBundle(val patchesJar: File) {
null
}
fun readManifestAttribute(name: String) = manifest?.mainAttributes?.getValue(name)
val patchBundleManifestAttributes = if(manifest != null)
PatchBundleManifestAttributes(
name = readManifestAttribute("name"),
version = readManifestAttribute("version"),
description = readManifestAttribute("description"),
source = readManifestAttribute("source"),
author = readManifestAttribute("author"),
contact = readManifestAttribute("contact"),
website = readManifestAttribute("website"),
license = readManifestAttribute("license")
) else
null
private fun readManifestAttribute(name: String) = manifest?.mainAttributes?.getValue(name)?.takeIf { it.isNotBlank() } // If empty, set it to null instead.
/**
* Load all patches compatible with the specified package.
@@ -53,4 +77,4 @@ class PatchBundle(val patchesJar: File) {
true
}
}
}

View File

@@ -2,13 +2,26 @@ package app.revanced.manager.ui.component.bundle
import android.webkit.URLUtil
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.outlined.ArrowRight
import androidx.compose.material.icons.outlined.Extension
import androidx.compose.material.icons.outlined.Inventory2
import androidx.compose.material.icons.automirrored.outlined.Send
import androidx.compose.material.icons.outlined.Commit
import androidx.compose.material.icons.outlined.Description
import androidx.compose.material.icons.outlined.Gavel
import androidx.compose.material.icons.outlined.Language
import androidx.compose.material.icons.outlined.Person
import androidx.compose.material.icons.outlined.Sell
import androidx.compose.material3.*
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
@@ -17,10 +30,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.patcher.patch.PatchBundleManifestAttributes
import app.revanced.manager.ui.component.ColumnWithScrollbar
import app.revanced.manager.ui.component.TextInputDialog
import app.revanced.manager.ui.component.haptics.HapticSwitch
@@ -29,12 +43,12 @@ import app.revanced.manager.ui.component.haptics.HapticSwitch
fun BaseBundleDialog(
modifier: Modifier = Modifier,
isDefault: Boolean,
name: String?,
remoteUrl: String?,
onRemoteUrlChange: ((String) -> Unit)? = null,
patchCount: Int,
version: String?,
autoUpdate: Boolean,
bundleManifestAttributes: PatchBundleManifestAttributes?,
onAutoUpdateChange: (Boolean) -> Unit,
onPatchesClick: () -> Unit,
extraFields: @Composable ColumnScope.() -> Unit = {}
@@ -48,35 +62,26 @@ fun BaseBundleDialog(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp, Alignment.Start),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = Icons.Outlined.Inventory2,
contentDescription = null,
tint = MaterialTheme.colorScheme.primary,
modifier = Modifier.size(32.dp)
)
name?.let {
Text(
text = it,
style = MaterialTheme.typography.titleLarge.copy(fontWeight = FontWeight(800)),
color = MaterialTheme.colorScheme.primary,
)
}
version?.let {
Tag(Icons.Outlined.Sell, it)
}
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.fillMaxWidth()
.padding(start = 2.dp)
) {
version?.let {
Tag(Icons.Outlined.Sell, it)
}
Tag(Icons.Outlined.Extension, patchCount.toString())
bundleManifestAttributes?.description?.let {
Tag(Icons.Outlined.Description, it)
}
bundleManifestAttributes?.source?.let {
Tag(Icons.Outlined.Commit, it)
}
bundleManifestAttributes?.author?.let {
Tag(Icons.Outlined.Person, it)
}
bundleManifestAttributes?.contact?.let {
Tag(Icons.AutoMirrored.Outlined.Send, it)
}
bundleManifestAttributes?.website?.let {
Tag(Icons.Outlined.Language, it, isUrl = true)
}
bundleManifestAttributes?.license?.let {
Tag(Icons.Outlined.Gavel, it)
}
}
@@ -138,8 +143,8 @@ fun BaseBundleDialog(
val patchesClickable = patchCount > 0
BundleListItem(
headlineText = stringResource(R.string.patches),
supportingText = stringResource(R.string.bundle_view_patches),
headlineText = stringResource(R.string.bundle_view_patches),
supportingText = stringResource(R.string.bundle_view_all_patches, patchCount),
modifier = Modifier.clickable(
enabled = patchesClickable,
onClick = onPatchesClick
@@ -160,22 +165,34 @@ fun BaseBundleDialog(
@Composable
private fun Tag(
icon: ImageVector,
text: String
text: String,
isUrl: Boolean = false
) {
val uriHandler = LocalUriHandler.current
Row(
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically,
modifier = if (isUrl) {
Modifier
.clickable {
try {
uriHandler.openUri(text)
} catch (_: Exception) {}
}
}
else
Modifier,
) {
Icon(
imageVector = icon,
contentDescription = null,
modifier = Modifier.size(16.dp),
tint = MaterialTheme.colorScheme.outline,
modifier = Modifier.size(16.dp)
)
Text(
text,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.outline,
color = if(isUrl) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.outline,
)
}
}

View File

@@ -44,13 +44,14 @@ fun BundleInformationDialog(
}.collectAsStateWithLifecycle(null)
val patchCount by bundle.patchCountFlow.collectAsStateWithLifecycle(0)
val version by bundle.versionFlow.collectAsStateWithLifecycle(null)
val bundleManifestAttributes = state.patchBundleOrNull()?.patchBundleManifestAttributes
if (viewCurrentBundlePatches) {
BundlePatchesDialog(
onDismissRequest = {
viewCurrentBundlePatches = false
},
bundle = bundle,
bundle = bundle
)
}
@@ -62,7 +63,7 @@ fun BundleInformationDialog(
Scaffold(
topBar = {
BundleTopBar(
title = stringResource(R.string.patch_bundle_field),
title = bundleName,
onBackClick = onDismissRequest,
backIcon = {
Icon(
@@ -94,11 +95,11 @@ fun BundleInformationDialog(
BaseBundleDialog(
modifier = Modifier.padding(paddingValues),
isDefault = bundle.isDefault,
name = bundleName,
remoteUrl = bundle.asRemoteOrNull?.endpoint,
patchCount = patchCount,
version = version,
autoUpdate = props?.autoUpdate == true,
bundleManifestAttributes = bundleManifestAttributes,
onAutoUpdateChange = {
composableScope.launch {
bundle.asRemoteOrNull?.setAutoUpdate(it)

View File

@@ -23,8 +23,6 @@ data class BundleInfo(
yieldAll(universal)
}
val patchCount get() = compatible.size + incompatible.size + universal.size
fun patchSequence(allowIncompatible: Boolean) = if (allowIncompatible) {
all
} else {
@@ -79,7 +77,7 @@ data class BundleInfo(
targetList.add(it)
}
BundleInfo(source.getName(), bundle.readManifestAttribute("Version"), source.uid, compatible, incompatible, universal)
BundleInfo(source.getName(), bundle.patchBundleManifestAttributes?.version, source.uid, compatible, incompatible, universal)
}
}

View File

@@ -354,6 +354,7 @@
<string name="bundle_auto_update">Auto update</string>
<string name="bundle_auto_update_description">Automatically update this bundle when ReVanced starts</string>
<string name="bundle_view_patches">View patches</string>
<string name="bundle_view_all_patches">View all %d patches</string>
<string name="bundle_view_patches_any_version">Any version</string>
<string name="bundle_view_patches_any_package">Any package</string>
<string name="bundle_delete_single_dialog_title">Delete bundle</string>