mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-18 00:33:58 +00:00
feat: Improve bundle info screen design (#2548)
This commit is contained in:
@@ -15,7 +15,7 @@ class LocalPatchBundle(name: String, id: Int, directory: File) :
|
||||
}
|
||||
|
||||
reload()?.also {
|
||||
saveVersionHash(it.readManifestAttribute("Version"))
|
||||
saveVersionHash(it.patchBundleManifestAttributes?.version)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user