Remove ConstraintLayout usage

This commit is contained in:
FooIbar committed 2025-06-01 16:03:29 +08:00
1 parent 286af0e5bf
commit 8f0404242d
5 files changed
+110 -255

No files matched your search

-1
View File
@@ -189,7 +189,6 @@ dependencies {
implementation(libs.androidx.core)
implementation(libs.androidx.core.splashscreen)
implementation(libs.androidx.constraintlayout.compose)
implementation(libs.androidx.datastore)
implementation(libs.androidx.graphics.path)
@@ -4,9 +4,13 @@ import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Pause
@@ -31,15 +35,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import arrow.core.Tuple9
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUtils
@@ -51,51 +49,6 @@ import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
import com.hippo.ehviewer.ui.tools.listThumbGenerator
import com.hippo.ehviewer.util.FileUtils
private val ids = Tuple9(1, 2, 3, 4, 5, 6, 7, 8, 9)
private val constraintSet = ConstraintSet {
val (
titleRef, uploaderRef, ratingRef, categoryRef, actionsRef, stateTextRef,
progressBarRef, progressTextRef, speedRef,
) = createRefsFor(1, 2, 3, 4, 5, 6, 7, 8, 9)
constrain(titleRef) {
top.linkTo(parent.top)
width = Dimension.matchParent
}
constrain(uploaderRef) {
start.linkTo(parent.start)
bottom.linkTo(actionsRef.top)
}
constrain(ratingRef) {
start.linkTo(parent.start)
top.linkTo(actionsRef.top, 1.dp)
}
constrain(categoryRef) {
start.linkTo(parent.start)
bottom.linkTo(parent.bottom)
}
constrain(progressBarRef) {
bottom.linkTo(actionsRef.top)
width = Dimension.matchParent
}
constrain(progressTextRef) {
bottom.linkTo(progressBarRef.top)
start.linkTo(progressBarRef.start)
}
constrain(speedRef) {
bottom.linkTo(progressBarRef.top)
end.linkTo(progressBarRef.end)
}
constrain(actionsRef) {
end.linkTo(parent.end, (-4).dp)
bottom.linkTo(parent.bottom, (-4).dp)
}
constrain(stateTextRef) {
end.linkTo(parent.end)
bottom.linkTo(actionsRef.top)
}
}
context(_: SharedTransitionScope, _: TransitionsVisibilityScope)
@Composable
fun DownloadCard(
@@ -132,52 +85,34 @@ fun DownloadCard(
val stateFailed = stringResource(R.string.download_state_failed)
val stateFailed2 = stringResource(R.string.download_state_failed_2, info.legacy)
val downloadState by DownloadManager.collectDownloadState(info.gid)
val stateText = when (downloadState) {
DownloadInfo.STATE_NONE -> stringResource(R.string.download_state_none)
DownloadInfo.STATE_WAIT -> stringResource(R.string.download_state_wait)
DownloadInfo.STATE_DOWNLOAD -> null
DownloadInfo.STATE_FAILED -> if (info.legacy <= 0) stateFailed else stateFailed2
DownloadInfo.STATE_FINISH -> stringResource(R.string.download_state_finish)
else -> null // Chill, will be removed soon
}
ConstraintLayout(
modifier = Modifier.padding(start = 8.dp, top = 2.dp, end = 4.dp, bottom = 4.dp).fillMaxSize(),
constraintSet = constraintSet,
) {
val (
titleRef, uploaderRef, ratingRef, categoryRef, actionsRef, stateTextRef,
progressBarRef, progressTextRef, speedRef,
) = ids
Column(modifier = Modifier.padding(start = 8.dp, top = 2.dp, end = 4.dp)) {
Text(
text = EhUtils.getSuitableTitle(info),
maxLines = 2,
modifier = Modifier.layoutId(titleRef),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleSmall,
)
Spacer(modifier = Modifier.weight(1f))
if (downloadState != DownloadInfo.STATE_DOWNLOAD) {
val stateText = when (downloadState) {
DownloadInfo.STATE_NONE -> stringResource(R.string.download_state_none)
DownloadInfo.STATE_WAIT -> stringResource(R.string.download_state_wait)
DownloadInfo.STATE_FAILED -> if (info.legacy <= 0) stateFailed else stateFailed2
DownloadInfo.STATE_FINISH -> stringResource(R.string.download_state_finish)
else -> null // The item has been removed and this will be disposed soon
}
ProvideTextStyle(MaterialTheme.typography.labelLarge) {
info.uploader?.let {
Row {
Text(
text = it,
modifier = Modifier.layoutId(uploaderRef).alpha(if (info.disowned) 0.5f else 1f),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = info.uploader.orEmpty(),
modifier = Modifier.alignByBaseline().alpha(if (info.disowned) 0.5f else 1f),
)
Spacer(modifier = Modifier.weight(1f))
Text(
text = stateText.orEmpty(),
modifier = Modifier.alignByBaseline(),
)
}
GalleryListCardRating(
rating = info.rating,
modifier = Modifier.layoutId(ratingRef),
)
val categoryColor = EhUtils.getCategoryColor(info.category)
val categoryText = EhUtils.getCategory(info.category).uppercase()
Text(
text = categoryText,
modifier = Modifier.layoutId(categoryRef).clip(ShapeDefaults.Small)
.background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
textAlign = TextAlign.Center,
)
}
} else {
var total by remember { mutableIntStateOf(info.total) }
@@ -190,44 +125,49 @@ fun DownloadCard(
}
ProvideTextStyle(MaterialTheme.typography.labelMedium) {
if (total <= 0 || finished < 0) {
LinearWavyProgressIndicator(modifier = Modifier.layoutId(progressBarRef))
LinearWavyProgressIndicator(modifier = Modifier.fillMaxWidth())
} else {
Row {
Text(text = "$finished/$total")
Spacer(modifier = Modifier.weight(1f))
Text(text = FileUtils.humanReadableByteCount(speed.coerceAtLeast(0)) + "/S")
}
LinearWavyProgressIndicator(
progress = { finished.toFloat() / total.toFloat() },
modifier = Modifier.layoutId(progressBarRef),
)
Text(
text = "$finished/$total",
modifier = Modifier.layoutId(progressTextRef),
modifier = Modifier.fillMaxWidth(),
)
}
}
}
Row {
Column {
// Place the rating near the uploader text as there's more visual space
GalleryListCardRating(rating = info.rating, modifier = Modifier.padding(top = 1.dp, bottom = 3.dp))
val categoryColor = EhUtils.getCategoryColor(info.category)
val categoryText = EhUtils.getCategory(info.category).uppercase()
Text(
text = FileUtils.humanReadableByteCount(speed.coerceAtLeast(0)) + "/S",
modifier = Modifier.layoutId(speedRef),
text = categoryText,
modifier = Modifier.clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
style = MaterialTheme.typography.labelLarge,
)
}
}
val running = downloadState == DownloadInfo.STATE_WAIT || downloadState == DownloadInfo.STATE_DOWNLOAD
val icon = remember {
movableContentOf<Boolean> {
Icon(imageVector = if (it) Icons.Default.Pause else Icons.Default.PlayArrow, contentDescription = null)
Spacer(modifier = Modifier.weight(1f))
val running = downloadState == DownloadInfo.STATE_WAIT || downloadState == DownloadInfo.STATE_DOWNLOAD
val icon = remember {
movableContentOf<Boolean> {
Icon(imageVector = if (it) Icons.Default.Pause else Icons.Default.PlayArrow, contentDescription = null)
}
}
}
if (selectMode) {
Box(modifier = Modifier.layoutId(actionsRef).minimumInteractiveComponentSize()) {
icon(running)
if (selectMode) {
Box(modifier = Modifier.offset(4.dp).minimumInteractiveComponentSize()) {
icon(running)
}
} else {
IconButton(onClick = if (running) onStop else onStart, modifier = Modifier.offset(4.dp)) {
icon(running)
}
}
} else {
IconButton(onClick = if (running) onStop else onStart, modifier = Modifier.layoutId(actionsRef)) {
icon(running)
}
}
if (stateText != null) {
Text(
text = stateText,
modifier = Modifier.layoutId(stateTextRef),
style = MaterialTheme.typography.labelLarge,
)
}
}
}
@@ -4,12 +4,14 @@ import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.IntrinsicSize
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.widthIn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.Label
@@ -29,7 +31,6 @@ import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.pluralStringResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import com.hippo.ehviewer.R
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.GalleryDetail
@@ -47,53 +48,29 @@ fun GalleryDetailHeaderInfoCard(
) = detail.run {
Card(
onClick = onClick,
modifier = modifier,
modifier = modifier.width(IntrinsicSize.Max),
) {
ConstraintLayout(modifier = Modifier.padding(8.dp)) {
val (langRef, sizeRef, favRef, pagesRef, postRef) = createRefs()
ProvideTextStyle(MaterialTheme.typography.labelMedium) {
Text(
text = language.orEmpty(),
modifier = Modifier.constrainAs(langRef) {
top.linkTo(parent.top)
start.linkTo(parent.start)
},
)
Text(
text = size.orEmpty(),
modifier = Modifier.constrainAs(sizeRef) {
baseline.linkTo(langRef.baseline)
end.linkTo(parent.end)
horizontalBias = 1.0f
start.linkTo(langRef.end)
}.padding(start = 16.dp),
)
ProvideTextStyle(MaterialTheme.typography.labelMedium) {
Row(modifier = Modifier.padding(8.dp)) {
Text(text = language.orEmpty())
Spacer(modifier = Modifier.width(16.dp).weight(1f))
Text(text = size.orEmpty())
}
Row(modifier = Modifier.padding(horizontal = 8.dp)) {
Text(
text = stringResource(id = R.string.favored_times, favoriteCount),
modifier = Modifier.constrainAs(favRef) {
start.linkTo(parent.start)
top.linkTo(langRef.bottom)
}.padding(top = 8.dp),
modifier = Modifier.alignByBaseline(),
)
Spacer(modifier = Modifier.width(16.dp).weight(1f))
Text(
text = pluralStringResource(id = R.plurals.page_count, pages, pages),
modifier = Modifier.constrainAs(pagesRef) {
baseline.linkTo(favRef.baseline)
end.linkTo(parent.end)
horizontalBias = 1.0f
start.linkTo(favRef.end)
}.padding(start = 16.dp),
)
Text(
text = posted.orEmpty(),
modifier = Modifier.constrainAs(postRef) {
end.linkTo(parent.end)
start.linkTo(parent.start)
top.linkTo(favRef.bottom)
}.padding(top = 8.dp),
modifier = Modifier.alignByBaseline(),
)
}
Text(
text = posted.orEmpty(),
modifier = Modifier.align(Alignment.CenterHorizontally).padding(8.dp),
)
}
}
}
@@ -5,10 +5,10 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
@@ -34,15 +34,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.layoutId
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.constraintlayout.compose.ConstraintLayout
import androidx.constraintlayout.compose.ConstraintSet
import androidx.constraintlayout.compose.Dimension
import arrow.core.Tuple7
import coil3.compose.AsyncImage
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.client.EhUtils
@@ -57,41 +51,6 @@ import com.hippo.ehviewer.ui.tools.TransitionsVisibilityScope
import com.hippo.ehviewer.ui.tools.listThumbGenerator
import com.hippo.ehviewer.util.FavouriteStatusRouter
private val ids = Tuple7(1, 2, 3, 4, 5, 6, 7)
private val constraintSet = ConstraintSet {
val (titleRef, uploaderRef, ratingRef, categoryRef, postedRef, favRef, iconsRef) =
createRefsFor(1, 2, 3, 4, 5, 6, 7)
constrain(titleRef) {
top.linkTo(parent.top)
width = Dimension.matchParent
}
constrain(uploaderRef) {
start.linkTo(parent.start)
bottom.linkTo(iconsRef.top)
}
constrain(ratingRef) {
start.linkTo(parent.start)
top.linkTo(iconsRef.top, 1.dp)
}
constrain(categoryRef) {
start.linkTo(parent.start)
bottom.linkTo(parent.bottom)
}
constrain(iconsRef) {
end.linkTo(parent.end)
bottom.linkTo(categoryRef.top)
}
constrain(favRef) {
end.linkTo(parent.end)
bottom.linkTo(iconsRef.top)
}
constrain(postedRef) {
end.linkTo(parent.end)
linkTo(categoryRef.top, parent.bottom)
}
}
context(_: SharedTransitionScope, _: TransitionsVisibilityScope)
@Composable
fun GalleryInfoListItem(
@@ -115,48 +74,48 @@ fun GalleryInfoListItem(
modifier = Modifier.aspectRatio(DEFAULT_RATIO),
)
}
ConstraintLayout(
modifier = Modifier.padding(start = 8.dp, top = 2.dp, end = 4.dp, bottom = 4.dp).fillMaxSize(),
constraintSet = constraintSet,
) {
val (titleRef, uploaderRef, ratingRef, categoryRef, postedRef, favRef, iconsRef) = ids
Column(modifier = Modifier.padding(start = 8.dp, top = 2.dp, end = 4.dp, bottom = 4.dp)) {
Text(
text = EhUtils.getSuitableTitle(info),
maxLines = 2,
modifier = Modifier.layoutId(titleRef),
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleSmall,
)
Spacer(modifier = Modifier.weight(1f))
ProvideTextStyle(MaterialTheme.typography.labelLarge) {
info.uploader?.let {
Row(horizontalArrangement = Arrangement.spacedBy(6.dp)) {
Text(
text = it,
modifier = Modifier.layoutId(uploaderRef).alpha(if (info.disowned) 0.5f else 1f),
maxLines = 1,
overflow = TextOverflow.Ellipsis,
text = info.uploader.orEmpty(),
modifier = Modifier.alignByBaseline().alpha(if (info.disowned) 0.5f else 1f),
)
Spacer(modifier = Modifier.weight(1f))
if (isInFavScene) {
info.favoriteNote?.let {
Text(text = it, modifier = Modifier.alignByBaseline(), fontStyle = FontStyle.Italic)
}
} else {
val showFav by FavouriteStatusRouter.collectAsState(info) { it != NOT_FAVORITED }
if (showFav) {
Icon(
Icons.Default.Favorite,
contentDescription = null,
modifier = Modifier.size(16.dp).align(Alignment.CenterVertically),
)
info.favoriteName?.let {
Text(text = it, modifier = Modifier.alignByBaseline())
}
}
}
}
GalleryListCardRating(
rating = info.rating,
modifier = Modifier.layoutId(ratingRef),
)
val categoryColor = EhUtils.getCategoryColor(info.category)
val categoryText = EhUtils.getCategory(info.category).uppercase()
Text(
text = categoryText,
modifier = Modifier.layoutId(categoryRef).clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
textAlign = TextAlign.Center,
)
Row(
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.layoutId(iconsRef),
) {
// Placeholder to reserve minimum height
Text(text = "")
val download by DownloadManager.collectContainDownloadInfo(info.gid)
if (download) {
// Place the rating near the uploader text as there's more visual space
GalleryListCardRating(rating = info.rating, modifier = Modifier.padding(top = 1.dp, bottom = 3.dp))
Spacer(modifier = Modifier.weight(1f))
val downloaded by DownloadManager.collectContainDownloadInfo(info.gid)
if (downloaded) {
Icon(
Icons.Default.Download,
contentDescription = null,
@@ -170,35 +129,17 @@ fun GalleryInfoListItem(
Text(text = "${info.pages}P")
}
}
Row(
horizontalArrangement = Arrangement.spacedBy(6.dp),
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.layoutId(favRef),
) {
// Placeholder to reserve minimum height
Text(text = "")
if (isInFavScene) {
info.favoriteNote?.let {
Text(text = it, fontStyle = FontStyle.Italic)
}
} else {
val showFav by FavouriteStatusRouter.collectAsState(info) { it != NOT_FAVORITED }
if (showFav) {
Icon(
Icons.Default.Favorite,
contentDescription = null,
modifier = Modifier.size(16.dp),
)
info.favoriteName?.let {
Text(text = it)
}
}
}
Row(verticalAlignment = Alignment.CenterVertically) {
val categoryColor = EhUtils.getCategoryColor(info.category)
val categoryText = EhUtils.getCategory(info.category).uppercase()
Text(
text = categoryText,
modifier = Modifier.clip(ShapeDefaults.Small).background(categoryColor).padding(vertical = 2.dp, horizontal = 8.dp),
color = if (Settings.harmonizeCategoryColor) Color.Unspecified else EhUtils.categoryTextColor,
)
Spacer(modifier = Modifier.weight(1f))
Text(text = info.posted.orEmpty())
}
Text(
text = info.posted.orEmpty(),
modifier = Modifier.layoutId(postedRef),
)
}
}
}
-2
View File
@@ -6,7 +6,6 @@ androidx-activity = "1.10.1"
androidx-appcompat = "1.7.0"
androidx-biometric = "1.4.0-alpha04"
androidx-browser = "1.8.0"
androidx-constraintlayout-compose = "1.1.1"
androidx-core = "1.16.0"
androidx-core-splashscreen = "1.2.0-beta02"
androidx-datastore = "1.1.7"
@@ -44,7 +43,6 @@ androidx-activity-compose = { module = "androidx.activity:activity-compose", ver
androidx-appcompat = { module = "androidx.appcompat:appcompat", version.ref = "androidx-appcompat" }
androidx-biometric = { module = "androidx.biometric:biometric", version.ref = "androidx-biometric" }
androidx-browser = { module = "androidx.browser:browser", version.ref = "androidx-browser" }
androidx-constraintlayout-compose = { module = "androidx.constraintlayout:constraintlayout-compose", version.ref = "androidx-constraintlayout-compose" }
androidx-core = { module = "androidx.core:core-ktx", version.ref = "androidx-core" }
androidx-core-splashscreen = { module = "androidx.core:core-splashscreen", version.ref = "androidx-core-splashscreen" }
androidx-datastore = { module = "androidx.datastore:datastore", version.ref = "androidx-datastore" }