feat: 优化设置许可证页面样式

remove: 全部的滚动条
This commit is contained in:
Super12138 2026-01-18 19:19:50 +08:00
parent 667cb152ea
commit 562293fd17
10 changed files with 333 additions and 434 deletions

View file

@ -38,7 +38,7 @@ android {
applicationId = "cn.super12138.todo"
minSdk = 24
targetSdk = 36
versionCode = 940
versionCode = 941
versionName = "2.3.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@ -101,12 +101,13 @@ dependencies {
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.animation)
implementation(libs.androidx.material3.adaptive)
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.android)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
implementation(libs.androidx.material3.windowsizeclass)
implementation(libs.androidx.material3.adaptive)
implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui)
// About Libraries
@ -118,8 +119,6 @@ dependencies {
implementation(libs.kyant0.capsule)
// Konfetti
implementation(libs.nl.dionsegijn.konfetti.compose)
// Lazy Column Scrollbar
implementation(libs.lazycolumnscrollbar)
// Kotlin
implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android)

View file

@ -1,25 +0,0 @@
package cn.super12138.todo.ui.components
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import my.nanihadesuka.compose.LazyColumnScrollbar
import my.nanihadesuka.compose.ScrollbarSettings
@Composable
fun LazyColumnCustomScrollBar(
state: LazyListState,
modifier: Modifier = Modifier,
content: @Composable () -> Unit
) {
LazyColumnScrollbar(
state = state,
modifier = modifier,
settings = ScrollbarSettings(
thumbUnselectedColor = MaterialTheme.colorScheme.secondary,
thumbSelectedColor = MaterialTheme.colorScheme.primary
),
content = content
)
}

View file

@ -1,7 +1,6 @@
package cn.super12138.todo.ui.pages.main
import androidx.activity.compose.BackHandler
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
@ -32,7 +31,7 @@ import cn.super12138.todo.ui.components.TodoFloatingActionButton
import cn.super12138.todo.ui.pages.main.components.TodoTopAppBar
import cn.super12138.todo.ui.viewmodels.MainViewModel
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalMaterial3ExpressiveApi::class)
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun MainPage(
modifier: Modifier = Modifier,

View file

@ -1,6 +1,5 @@
package cn.super12138.todo.ui.pages.main
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
@ -19,10 +18,9 @@ import cn.super12138.todo.R
import cn.super12138.todo.logic.database.TodoEntity
import cn.super12138.todo.logic.model.Priority
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.LazyColumnCustomScrollBar
import cn.super12138.todo.ui.pages.main.components.TodoCard
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalMaterial3ExpressiveApi::class)
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ManagerFragment(
modifier: Modifier = Modifier,
@ -33,60 +31,55 @@ fun ManagerFragment(
onItemChecked: (TodoEntity) -> Unit = {},
selectedTodoIds: List<Int>
) {
LazyColumnCustomScrollBar(
LazyColumn(
state = state,
contentPadding = PaddingValues(
start = TodoDefaults.screenHorizontalPadding,
bottom = TodoDefaults.toDoCardHeight / 2,
end = TodoDefaults.screenHorizontalPadding
),
modifier = modifier
) {
LazyColumn(
state = state,
contentPadding = PaddingValues(
start = TodoDefaults.screenHorizontalPadding,
bottom = TodoDefaults.toDoCardHeight / 2,
end = TodoDefaults.screenHorizontalPadding
),
/*modifier = Modifier
.clip(
MaterialTheme.shapes.extraLarge.copy(
bottomEnd = CornerSize(0.dp),
bottomStart = CornerSize(0.dp)
)
/*.clip(
MaterialTheme.shapes.extraLarge.copy(
bottomEnd = CornerSize(0.dp),
bottomStart = CornerSize(0.dp)
)
.background(MaterialTheme.colorScheme.surfaceContainerHighest)*/
// verticalArrangement = Arrangement.spacedBy(10.dp)
) {
if (list.isEmpty()) {
item {
Text(
text = stringResource(R.string.tip_no_task),
style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
} else {
items(
items = list,
key = { it.id }
) { item ->
TodoCard(
// id = item.id,
content = item.content,
category = item.category,
completed = item.isCompleted,
priority = Priority.fromFloat(item.priority),
selected = selectedTodoIds.contains(item.id),
onCardClick = { onItemClick(item) },
onCardLongClick = { onItemLongClick(item) },
onChecked = { onItemChecked(item) },
modifier = Modifier
.padding(vertical = 5.dp)
.animateItem(
fadeInSpec = MaterialTheme.motionScheme.defaultEffectsSpec(),
placementSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
fadeOutSpec = MaterialTheme.motionScheme.fastEffectsSpec()
)
)
}
)
.background(MaterialTheme.colorScheme.surfaceContainerHighest)*/
// verticalArrangement = Arrangement.spacedBy(10.dp)
) {
if (list.isEmpty()) {
item {
Text(
text = stringResource(R.string.tip_no_task),
style = MaterialTheme.typography.titleMedium,
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
}
} else {
items(
items = list,
key = { it.id }
) { item ->
TodoCard(
// id = item.id,
content = item.content,
category = item.category,
completed = item.isCompleted,
priority = Priority.fromFloat(item.priority),
selected = selectedTodoIds.contains(item.id),
onCardClick = { onItemClick(item) },
onCardLongClick = { onItemLongClick(item) },
onChecked = { onItemChecked(item) },
modifier = Modifier
.padding(vertical = 5.dp)
.animateItem(
fadeInSpec = MaterialTheme.motionScheme.defaultEffectsSpec(),
placementSpec = MaterialTheme.motionScheme.defaultSpatialSpec(),
fadeOutSpec = MaterialTheme.motionScheme.fastEffectsSpec()
)
)
}
}
}

View file

@ -1,46 +1,156 @@
package cn.super12138.todo.ui.pages.settings
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Badge
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalButton
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
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.platform.LocalUriHandler
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.BasicDialog
import cn.super12138.todo.ui.components.RoundedScreenContainer
import cn.super12138.todo.ui.components.TopAppBarScaffold
import cn.super12138.todo.ui.pages.settings.components.licence.LicenceList
import cn.super12138.todo.ui.pages.settings.components.SettingsContainer
import cn.super12138.todo.ui.pages.settings.components.SettingsItem
import cn.super12138.todo.utils.VibrationUtils
import com.mikepenz.aboutlibraries.ui.compose.android.produceLibraries
import com.mikepenz.aboutlibraries.ui.compose.util.author
import com.mikepenz.aboutlibraries.ui.compose.util.htmlReadyLicenseContent
@OptIn(ExperimentalMaterial3Api::class)
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun SettingsAboutLicence(
onNavigateUp: () -> Unit,
modifier: Modifier = Modifier
) {
val libraries by produceLibraries(R.raw.aboutlibraries)
val view = LocalView.current
val uriHandler = LocalUriHandler.current
TopAppBarScaffold(
title = stringResource(R.string.pref_licence),
onBack = onNavigateUp,
modifier = modifier
) { innerPadding ->
val libraries by produceLibraries(R.raw.aboutlibraries)
Box(
modifier = Modifier
.fillMaxSize()
.padding(innerPadding)
.padding(horizontal = TodoDefaults.screenHorizontalPadding)
.clip(MaterialTheme.shapes.extraLarge)
) {
val listState = rememberLazyListState()
LicenceList(
libraries = libraries,
state = listState
)
RoundedScreenContainer(Modifier.padding(innerPadding)) {
SettingsContainer(Modifier.fillMaxSize()) {
itemsIndexed(
items = libraries?.libraries ?: listOf(),
key = { _, library -> library.artifactId }
) { index, library ->
var openDialog by rememberSaveable { mutableStateOf(false) }
SettingsItem(
headlineContent = {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = library.name,
modifier = Modifier.weight(1f),
style = MaterialTheme.typography.titleLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
val version = library.artifactVersion
if (version != null) {
Text(
text = version,
style = MaterialTheme.typography.bodyMedium,
textAlign = TextAlign.Center
)
}
}
val author = library.author
if (author.isNotBlank()) {
Text(
text = author,
style = MaterialTheme.typography.bodyMedium
)
}
},
supportingContent = {
if (library.licenses.isNotEmpty()) {
FlowRow {
library.licenses.forEach {
Badge(containerColor = MaterialTheme.colorScheme.primary) {
Text(
maxLines = 1,
text = it.name,
// style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center,
overflow = TextOverflow.Ellipsis,
)
}
}
}
}
},
onClick = {
val license = library.licenses.firstOrNull()
if (!license?.htmlReadyLicenseContent.isNullOrBlank()) {
openDialog = true
} else if (!license?.url.isNullOrBlank()) {
license.url?.also {
try {
uriHandler.openUri(it)
} catch (t: Throwable) {
throw Exception("Failed to open licence URL: $it", t)
}
}
}
},
topRounded = index == 0,
bottomRounded = index == (libraries?.libraries?.size ?: 1) - 1
)
BasicDialog(
visible = openDialog,
title = { Text(library.name) },
text = {
library.licenses.firstOrNull()?.licenseContent?.let {
Column(Modifier.verticalScroll(rememberScrollState())) {
SelectionContainer { Text(text = it) }
}
}
},
confirmButton = {
FilledTonalButton(
onClick = {
openDialog = false
VibrationUtils.performHapticFeedback(view)
},
shapes = ButtonDefaults.shapes()
) { Text(stringResource(R.string.action_confirm)) }
},
onDismissRequest = { openDialog = false }
)
}
}
}
}
}

View file

@ -1,6 +1,5 @@
package cn.super12138.todo.ui.pages.settings.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@ -43,7 +42,7 @@ fun SettingsRadioDialog(
Column(
verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = Modifier
.clip(MaterialTheme.shapes.extraLarge)
.clip(TodoDefaults.SettingsItemRoundedShape)
.selectableGroup()
.verticalScroll(rememberScrollState())
) {
@ -77,7 +76,7 @@ fun RadioItem(
.fillMaxWidth()
.height(56.dp)
.clip(TodoDefaults.SettingsItemDefaultShape)
.background(MaterialTheme.colorScheme.surfaceContainerHighest)
// .background(MaterialTheme.colorScheme.surfaceContainerHighest)
.selectable(
selected = selected,
onClick = {

View file

@ -28,6 +28,7 @@ import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.utils.VibrationUtils
import cn.super12138.todo.utils.getPartialRoundedShape
// Leading icon as drawable resource
@Composable
fun SettingsItem(
modifier: Modifier = Modifier,
@ -38,44 +39,20 @@ fun SettingsItem(
bottomRounded: Boolean = false,
enableClick: Boolean = true,
onClick: () -> Unit = {}
) {
SettingsItem(
leadingIcon = painterResource(leadingIconRes),
title = title,
description = description,
trailingContent = null,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
}
) = SettingsItem(
leadingIcon = painterResource(leadingIconRes),
title = title,
description = description,
trailingContent = null,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
@Composable
fun SettingsItem(
modifier: Modifier = Modifier,
leadingIcon: ImageVector? = null,
title: String,
description: String? = null,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true,
onClick: () -> Unit = {}
) {
SettingsItem(
leadingIcon = leadingIcon,
title = title,
description = description,
trailingContent = null,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun SettingsItem(
modifier: Modifier = Modifier,
@ -88,29 +65,61 @@ fun SettingsItem(
bottomRounded: Boolean = false,
enableClick: Boolean = true,
onClick: () -> Unit = {}
) {
SettingsItem(
leadingIcon = {
leadingIcon?.let {
Icon(
painter = leadingIcon,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding),
)
}
},
title = title,
description = description,
trailingContent = trailingContent,
background = background,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
}
) = SettingsItem(
leadingIcon = {
leadingIcon?.let {
/*Box(
modifier = Modifier
.padding(end = TodoDefaults.settingsItemHorizontalPadding)
.background(
color = MaterialTheme.colorScheme.secondaryContainer,
shape = MaterialShapes.Cookie6Sided.toShape()
)
.size(35.dp),
contentAlignment = Alignment.Center
) {*/
Icon(
painter = leadingIcon,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding)
)
// }
}
},
title = title,
description = description,
trailingContent = trailingContent,
background = background,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
// Leading icon as ImageVector
@Composable
fun SettingsItem(
modifier: Modifier = Modifier,
leadingIcon: ImageVector? = null,
title: String,
description: String? = null,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true,
onClick: () -> Unit = {}
) = SettingsItem(
leadingIcon = leadingIcon,
title = title,
description = description,
trailingContent = null,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
@Composable
fun SettingsItem(
@ -124,29 +133,28 @@ fun SettingsItem(
bottomRounded: Boolean = false,
enableClick: Boolean = true,
onClick: () -> Unit = {}
) {
SettingsItem(
leadingIcon = {
leadingIcon?.let {
Icon(
imageVector = it,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding),
)
}
},
title = title,
description = description,
trailingContent = trailingContent,
background = background,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
}
) = SettingsItem(
leadingIcon = {
leadingIcon?.let {
Icon(
imageVector = it,
contentDescription = null,
tint = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding),
)
}
},
title = title,
description = description,
trailingContent = trailingContent,
background = background,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick,
modifier = modifier
)
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
@ -163,6 +171,57 @@ fun SettingsItem(
bottomRounded: Boolean = false,
enableClick: Boolean = true,
onClick: () -> Unit = {},
) = SettingsItem(
modifier = modifier,
leadingIcon = leadingIcon,
headlineContent = {
Text(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge.copy(
color = MaterialTheme.colorScheme.onSurface,
fontSize = 20.sp
)
)
},
supportingContent = {
description?.let {
Text(
text = it,
// maxLines = 2,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant
)
)
}
},
trailingContent = trailingContent,
background = background,
shape = shape,
roundedShape = roundedShape,
topRounded = topRounded,
bottomRounded = bottomRounded,
enableClick = enableClick,
onClick = onClick
)
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun SettingsItem(
modifier: Modifier = Modifier,
leadingIcon: (@Composable () -> Unit)? = null,
headlineContent: (@Composable () -> Unit)? = null,
supportingContent: (@Composable () -> Unit)? = null,
trailingContent: (@Composable () -> Unit)? = null,
background: Color = TodoDefaults.ContainerColor,
shape: CornerBasedShape = TodoDefaults.SettingsItemDefaultShape,
roundedShape: CornerBasedShape = TodoDefaults.SettingsItemRoundedShape,
topRounded: Boolean = false,
bottomRounded: Boolean = false,
enableClick: Boolean = true,
onClick: () -> Unit = {},
) {
val view = LocalView.current
Row(
@ -171,47 +230,23 @@ fun SettingsItem(
.wrapContentHeight()
.clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape))
.clickable(
enabled = enableClick,
onClick = {
enabled = enableClick, onClick = {
VibrationUtils.performHapticFeedback(view)
onClick()
}
)
})
.background(background)
.padding(
horizontal = TodoDefaults.settingsItemHorizontalPadding,
vertical = TodoDefaults.settingsItemVerticalPadding
),
verticalAlignment = Alignment.CenterVertically
), verticalAlignment = Alignment.CenterVertically
) {
leadingIcon?.let {
it()
}
leadingIcon?.let { it() }
Column(modifier = Modifier.weight(1f)) {
Text(
text = title,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleLarge.copy(
color = MaterialTheme.colorScheme.onSurface,
fontSize = 20.sp
)
)
description?.let {
Text(
text = it,
// maxLines = 2,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.bodyMedium.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant
)
)
}
headlineContent?.let { it() }
supportingContent?.let { it() }
}
trailingContent?.let {
it()
}
trailingContent?.let { it() }
}
}

View file

@ -1,108 +0,0 @@
package cn.super12138.todo.ui.pages.settings.components.licence
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.Typography
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import com.mikepenz.aboutlibraries.entity.Library
import com.mikepenz.aboutlibraries.ui.compose.LibraryColors
import com.mikepenz.aboutlibraries.ui.compose.LibraryDefaults
import com.mikepenz.aboutlibraries.ui.compose.LibraryPadding
import com.mikepenz.aboutlibraries.ui.compose.m3.component.LibraryChip
import com.mikepenz.aboutlibraries.ui.compose.m3.libraryColors
import com.mikepenz.aboutlibraries.ui.compose.util.author
@OptIn(ExperimentalLayoutApi::class)
@Composable
fun LicenceItem(
modifier: Modifier = Modifier,
library: Library,
showAuthor: Boolean = true,
showVersion: Boolean = true,
showLicenseBadges: Boolean = true,
colors: LibraryColors = LibraryDefaults.libraryColors(
libraryBackgroundColor = MaterialTheme.colorScheme.surfaceContainerHigh
),
padding: LibraryPadding = LibraryDefaults.libraryPadding(),
libraryPadding: LibraryPadding = LibraryDefaults.libraryPadding(),
typography: Typography = MaterialTheme.typography,
shape: Shape = MaterialTheme.shapes.small,
onClick: () -> Unit
) {
Column(
modifier = modifier
.fillMaxWidth()
.clip(shape)
.clickable { onClick.invoke() }
.background(colors.libraryBackgroundColor)
.padding(libraryPadding.contentPadding)
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = library.name,
modifier = Modifier
.padding(padding.namePadding)
.weight(1f),
style = typography.titleLarge,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
val version = library.artifactVersion
if (version != null && showVersion) {
Text(
text = version,
modifier = Modifier.padding(padding.versionPadding.contentPadding),
style = typography.bodyMedium,
textAlign = TextAlign.Center
)
}
}
val author = library.author
if (showAuthor && author.isNotBlank()) {
Text(
text = author,
style = typography.bodyMedium
)
}
if (showLicenseBadges && library.licenses.isNotEmpty()) {
FlowRow {
library.licenses.forEach {
LibraryChip(
modifier = Modifier.padding(padding.licensePadding.containerPadding),
minHeight = LibraryDefaults.libraryDimensions().chipMinHeight,
containerColor = colors.licenseChipColors.containerColor,
contentColor = colors.licenseChipColors.contentColor,
shape = LibraryDefaults.libraryShapes().chipShape,
) {
Text(
modifier = Modifier.padding(padding.licensePadding.contentPadding),
maxLines = 1,
text = it.name,
// style = MaterialTheme.typography.labelSmall,
textAlign = TextAlign.Center,
overflow = LibraryDefaults.libraryTextStyles().defaultOverflow,
)
}
}
}
}
}
}

View file

@ -1,101 +0,0 @@
package cn.super12138.todo.ui.pages.settings.components.licence
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.text.selection.SelectionContainer
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.components.BasicDialog
import cn.super12138.todo.ui.components.LazyColumnCustomScrollBar
import cn.super12138.todo.utils.VibrationUtils
import com.mikepenz.aboutlibraries.Libs
import com.mikepenz.aboutlibraries.ui.compose.util.htmlReadyLicenseContent
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun LicenceList(
modifier: Modifier = Modifier,
libraries: Libs?,
state: LazyListState = rememberLazyListState()
) {
val view = LocalView.current
val uriHandler = LocalUriHandler.current
LazyColumnCustomScrollBar(
state = state,
modifier = modifier
) {
LazyColumn(
state = state,
verticalArrangement = Arrangement.spacedBy(2.dp),
contentPadding = PaddingValues(vertical = TodoDefaults.screenVerticalPadding),
modifier = modifier
) {
items(items = libraries?.libraries ?: listOf(), key = { it.artifactId }) { library ->
var openDialog by rememberSaveable { mutableStateOf(false) }
LicenceItem(
library = library,
onClick = {
val license = library.licenses.firstOrNull()
if (!license?.htmlReadyLicenseContent.isNullOrBlank()) {
openDialog = true
} else if (!license?.url.isNullOrBlank()) {
license.url?.also {
try {
uriHandler.openUri(it)
} catch (t: Throwable) {
throw Exception("Failed to open licence URL: $it", t)
}
}
}
VibrationUtils.performHapticFeedback(view)
},
)
BasicDialog(
visible = openDialog,
title = { Text(library.name) },
text = {
library.licenses.firstOrNull()?.licenseContent?.let {
Column(Modifier.verticalScroll(rememberScrollState())) {
SelectionContainer { Text(text = it) }
}
}
},
confirmButton = {
FilledTonalButton(
onClick = {
openDialog = false
VibrationUtils.performHapticFeedback(view)
},
shapes = ButtonDefaults.shapes()
) { Text(stringResource(R.string.action_confirm)) }
},
onDismissRequest = { openDialog = false }
)
}
}
}
}

View file

@ -48,7 +48,6 @@ androidx-datastore-preferences = { group = "androidx.datastore", name = "datasto
# Compose
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-animation = { group = "androidx.compose.animation", name = "animation" }
androidx-material3-adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version.ref = "adaptive" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-android = { group = "androidx.compose.ui", name = "ui-android" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
@ -57,6 +56,8 @@ androidx-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-toolin
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-material3-windowsizeclass = { group = "androidx.compose.material3", name = "material3-window-size-class", version.ref = "material3" }
androidx-material3-adaptive = { group = "androidx.compose.material3", name = "material3-adaptive-navigation-suite", version.ref = "material3" }
androidx-navigation3-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", version.ref = "nav3Core" }
@ -77,9 +78,6 @@ kyant0-capsule = { group = "io.github.kyant0", name = "capsule", version.ref = "
# Konfetti
nl-dionsegijn-konfetti-compose = { group = "nl.dionsegijn", name = "konfetti-compose", version.ref = "konfetti" }
# Lazy Column Scrollbar
lazycolumnscrollbar = { group = "com.github.nanihadesuka", name = "LazyColumnScrollbar", version.ref = "lazyColumnScrollbar" }
# Kotlin
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinCoroutines" }
kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinCoroutines" }