mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 05:54:26 +02:00
feat: View bundle patches (#2065)
This commit is contained in:
parent
10e7e4b39f
commit
2055400565
@ -1,33 +1,35 @@
|
|||||||
package app.revanced.manager.ui.component.bundle
|
package app.revanced.manager.ui.component.bundle
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.layout.padding
|
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.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.outlined.Lightbulb
|
import androidx.compose.material3.*
|
||||||
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.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.saveable.rememberSaveable
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
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.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.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.domain.bundles.PatchBundleSource
|
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.LazyColumnWithScrollbar
|
||||||
import app.revanced.manager.ui.component.NotificationCard
|
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -35,7 +37,8 @@ fun BundlePatchesDialog(
|
|||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
bundle: PatchBundleSource,
|
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()
|
val state by bundle.state.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
Dialog(
|
Dialog(
|
||||||
@ -62,44 +65,212 @@ fun BundlePatchesDialog(
|
|||||||
LazyColumnWithScrollbar(
|
LazyColumnWithScrollbar(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.padding(paddingValues)
|
.padding(paddingValues),
|
||||||
.padding(16.dp)
|
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 ->
|
state.patchBundleOrNull()?.let { bundle ->
|
||||||
items(bundle.patches.size) { bundleIndex ->
|
items(bundle.patches) { patch ->
|
||||||
val patch = bundle.patches[bundleIndex]
|
PatchItem(
|
||||||
ListItem(
|
patch,
|
||||||
headlineContent = {
|
showAllVersions,
|
||||||
Text(
|
onExpandVersions = { showAllVersions = !showAllVersions },
|
||||||
text = patch.name,
|
showOptions,
|
||||||
style = MaterialTheme.typography.bodyLarge,
|
onExpandOptions = { showOptions = !showOptions }
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
)
|
|
||||||
},
|
|
||||||
supportingContent = {
|
|
||||||
patch.description?.let {
|
|
||||||
Text(
|
|
||||||
text = it,
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
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"
|
@ -287,6 +287,7 @@
|
|||||||
<string name="drag_handle">reorder</string>
|
<string name="drag_handle">reorder</string>
|
||||||
|
|
||||||
<string name="more">More</string>
|
<string name="more">More</string>
|
||||||
|
<string name="less">Less</string>
|
||||||
<string name="continue_">Continue</string>
|
<string name="continue_">Continue</string>
|
||||||
<string name="dismiss">Dismiss</string>
|
<string name="dismiss">Dismiss</string>
|
||||||
<string name="permanent_dismiss">Do not show this again</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">Auto update</string>
|
||||||
<string name="bundle_auto_update_description">Automatically update this bundle when ReVanced starts</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">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="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="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>
|
<string name="update_available">An update is available</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user