feat: View bundle patches (#2065)

This commit is contained in:
Ushie 2024-08-25 01:23:13 +03:00 committed by GitHub
parent 10e7e4b39f
commit 2055400565
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 220 additions and 45 deletions

View file

@ -1,33 +1,35 @@
package app.revanced.manager.ui.component.bundle
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.outlined.Lightbulb
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.ListItem
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import app.revanced.manager.R
import app.revanced.manager.domain.bundles.PatchBundleSource
import app.revanced.manager.patcher.patch.PatchInfo
import app.revanced.manager.ui.component.ArrowButton
import app.revanced.manager.ui.component.LazyColumnWithScrollbar
import app.revanced.manager.ui.component.NotificationCard
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -35,7 +37,8 @@ fun BundlePatchesDialog(
onDismissRequest: () -> Unit,
bundle: PatchBundleSource,
) {
var informationCardVisible by remember { mutableStateOf(true) }
var showAllVersions by rememberSaveable { mutableStateOf(false) }
var showOptions by rememberSaveable { mutableStateOf(false) }
val state by bundle.state.collectAsStateWithLifecycle()
Dialog(
@ -62,44 +65,212 @@ fun BundlePatchesDialog(
LazyColumnWithScrollbar(
modifier = Modifier
.fillMaxWidth()
.padding(paddingValues)
.padding(16.dp)
.padding(paddingValues),
verticalArrangement = Arrangement.spacedBy(12.dp),
contentPadding = PaddingValues(16.dp)
) {
item {
AnimatedVisibility(visible = informationCardVisible) {
NotificationCard(
icon = Icons.Outlined.Lightbulb,
text = stringResource(R.string.tap_on_patches),
onDismiss = { informationCardVisible = false }
)
}
}
state.patchBundleOrNull()?.let { bundle ->
items(bundle.patches.size) { bundleIndex ->
val patch = bundle.patches[bundleIndex]
ListItem(
headlineContent = {
Text(
text = patch.name,
style = MaterialTheme.typography.bodyLarge,
color = MaterialTheme.colorScheme.onSurface
)
},
supportingContent = {
patch.description?.let {
Text(
text = it,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
items(bundle.patches) { patch ->
PatchItem(
patch,
showAllVersions,
onExpandVersions = { showAllVersions = !showAllVersions },
showOptions,
onExpandOptions = { showOptions = !showOptions }
)
HorizontalDivider()
}
}
}
}
}
}
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun PatchItem(
patch: PatchInfo,
expandVersions: Boolean,
onExpandVersions: () -> Unit,
expandOptions: Boolean,
onExpandOptions: () -> Unit
) {
ElevatedCard(
modifier = Modifier
.fillMaxWidth()
.then(
if (patch.options.isNullOrEmpty()) Modifier else Modifier
.clip(RoundedCornerShape(8.dp))
.clickable(onClick = onExpandOptions),
)
) {
Column(
modifier = Modifier.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.Absolute.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = patch.name,
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold
)
if (!patch.options.isNullOrEmpty()) {
ArrowButton(expanded = expandOptions, onClick = null)
}
}
patch.description?.let {
Text(
text = it,
style = MaterialTheme.typography.bodyMedium
)
}
Column(
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
if (patch.compatiblePackages.isNullOrEmpty()) {
Row(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalAlignment = Alignment.CenterVertically
) {
PatchInfoChip(
text = "$PACKAGE_ICON ${stringResource(R.string.bundle_view_patches_any_package)}"
)
PatchInfoChip(
text = "$VERSION_ICON ${stringResource(R.string.bundle_view_patches_any_version)}"
)
}
} else {
patch.compatiblePackages.forEach { compatiblePackage ->
val packageName = compatiblePackage.packageName
val versions = compatiblePackage.versions.orEmpty().reversed()
FlowRow(
horizontalArrangement = Arrangement.spacedBy(4.dp),
verticalArrangement = Arrangement.spacedBy(4.dp)
) {
PatchInfoChip(
modifier = Modifier.align(Alignment.CenterVertically),
text = "$PACKAGE_ICON $packageName"
)
if (versions.isNotEmpty()) {
if (expandVersions) {
versions.forEach { version ->
PatchInfoChip(
modifier = Modifier.align(Alignment.CenterVertically),
text = "$VERSION_ICON $version"
)
}
} else {
PatchInfoChip(
modifier = Modifier.align(Alignment.CenterVertically),
text = "$VERSION_ICON ${versions.first()}"
)
}
if (versions.size > 1) {
PatchInfoChip(
onClick = onExpandVersions,
text = if (expandVersions) stringResource(R.string.less) else "+${versions.size - 1}"
)
}
}
}
}
}
}
if (!patch.options.isNullOrEmpty()) {
AnimatedVisibility(visible = expandOptions) {
val options = patch.options
Column {
options.forEachIndexed { i, option ->
OutlinedCard(
modifier = Modifier.fillMaxWidth(),
colors = CardColors(
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onSurface,
disabledContainerColor = Color.Transparent,
disabledContentColor = MaterialTheme.colorScheme.onSurface
), shape = when {
options.size == 1 -> RoundedCornerShape(8.dp)
i == 0 -> RoundedCornerShape(topStart = 8.dp, topEnd = 8.dp)
i == options.lastIndex -> RoundedCornerShape(
bottomStart = 8.dp,
bottomEnd = 8.dp
)
else -> RoundedCornerShape(0.dp)
}
) {
Column(
modifier = Modifier.padding(12.dp),
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
Text(
text = option.title,
style = MaterialTheme.typography.titleMedium,
color = MaterialTheme.colorScheme.primary
)
Text(
text = option.description,
style = MaterialTheme.typography.bodyMedium
)
}
}
}
}
}
}
}
}
}
@Composable
fun PatchInfoChip(
modifier: Modifier = Modifier,
onClick: (() -> Unit)? = null,
text: String
) {
val shape = RoundedCornerShape(8.0.dp)
val cardModifier = if (onClick != null) {
Modifier
.clip(shape)
.clickable(onClick = onClick)
} else {
Modifier
}
OutlinedCard(
modifier = modifier.then(cardModifier),
colors = CardColors(
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onSurface,
disabledContainerColor = Color.Transparent,
disabledContentColor = MaterialTheme.colorScheme.onSurface
),
shape = shape,
border = BorderStroke(1.dp, MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.20f))
) {
Row(
modifier = Modifier.padding(8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Text(
text,
overflow = TextOverflow.Ellipsis,
softWrap = false,
style = MaterialTheme.typography.labelLarge,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
}
}
}
const val PACKAGE_ICON = "\uD83D\uDCE6"
const val VERSION_ICON = "\uD83C\uDFAF"

View file

@ -287,6 +287,7 @@
<string name="drag_handle">reorder</string>
<string name="more">More</string>
<string name="less">Less</string>
<string name="continue_">Continue</string>
<string name="dismiss">Dismiss</string>
<string name="permanent_dismiss">Do not show this again</string>
@ -305,6 +306,9 @@
<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_patches_any_version">Any version</string>
<string name="bundle_view_patches_any_package">Any package</string>
<string name="about_revanced_manager">About ReVanced Manager</string>
<string name="revanced_manager_description">ReVanced Manager is an application designed to work with ReVanced Patcher, which allows for long-lasting patches to be created for Android apps. The patching system is designed to automatically work with new versions of apps with minimal maintenance.</string>
<string name="update_available">An update is available</string>