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" applicationId = "cn.super12138.todo"
minSdk = 24 minSdk = 24
targetSdk = 36 targetSdk = 36
versionCode = 940 versionCode = 941
versionName = "2.3.3" versionName = "2.3.3"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
@ -101,12 +101,13 @@ dependencies {
implementation(libs.androidx.activity.compose) implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom)) implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.animation) implementation(libs.androidx.animation)
implementation(libs.androidx.material3.adaptive)
implementation(libs.androidx.ui) implementation(libs.androidx.ui)
implementation(libs.androidx.ui.android) implementation(libs.androidx.ui.android)
implementation(libs.androidx.ui.graphics) implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview) implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3) implementation(libs.androidx.material3)
implementation(libs.androidx.material3.windowsizeclass)
implementation(libs.androidx.material3.adaptive)
implementation(libs.androidx.navigation3.runtime) implementation(libs.androidx.navigation3.runtime)
implementation(libs.androidx.navigation3.ui) implementation(libs.androidx.navigation3.ui)
// About Libraries // About Libraries
@ -118,8 +119,6 @@ dependencies {
implementation(libs.kyant0.capsule) implementation(libs.kyant0.capsule)
// Konfetti // Konfetti
implementation(libs.nl.dionsegijn.konfetti.compose) implementation(libs.nl.dionsegijn.konfetti.compose)
// Lazy Column Scrollbar
implementation(libs.lazycolumnscrollbar)
// Kotlin // Kotlin
implementation(libs.kotlinx.coroutines.core) implementation(libs.kotlinx.coroutines.core)
implementation(libs.kotlinx.coroutines.android) 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 package cn.super12138.todo.ui.pages.main
import androidx.activity.compose.BackHandler import androidx.activity.compose.BackHandler
import androidx.compose.animation.ExperimentalSharedTransitionApi
import androidx.compose.animation.SharedTransitionScope import androidx.compose.animation.SharedTransitionScope
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets 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.pages.main.components.TodoTopAppBar
import cn.super12138.todo.ui.viewmodels.MainViewModel import cn.super12138.todo.ui.viewmodels.MainViewModel
@OptIn(ExperimentalSharedTransitionApi::class, ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
fun MainPage( fun MainPage(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,

View file

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

View file

@ -1,46 +1,156 @@
package cn.super12138.todo.ui.pages.settings 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.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.material3.ExperimentalMaterial3Api 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.MaterialTheme
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.saveable.rememberSaveable
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.platform.LocalUriHandler
import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource 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.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.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.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 @Composable
fun SettingsAboutLicence( fun SettingsAboutLicence(
onNavigateUp: () -> Unit, onNavigateUp: () -> Unit,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val libraries by produceLibraries(R.raw.aboutlibraries)
val view = LocalView.current
val uriHandler = LocalUriHandler.current
TopAppBarScaffold( TopAppBarScaffold(
title = stringResource(R.string.pref_licence), title = stringResource(R.string.pref_licence),
onBack = onNavigateUp, onBack = onNavigateUp,
modifier = modifier modifier = modifier
) { innerPadding -> ) { innerPadding ->
val libraries by produceLibraries(R.raw.aboutlibraries)
Box( RoundedScreenContainer(Modifier.padding(innerPadding)) {
modifier = Modifier SettingsContainer(Modifier.fillMaxSize()) {
.fillMaxSize() itemsIndexed(
.padding(innerPadding) items = libraries?.libraries ?: listOf(),
.padding(horizontal = TodoDefaults.screenHorizontalPadding) key = { _, library -> library.artifactId }
.clip(MaterialTheme.shapes.extraLarge) ) { index, library ->
) { var openDialog by rememberSaveable { mutableStateOf(false) }
val listState = rememberLazyListState()
LicenceList( SettingsItem(
libraries = libraries, headlineContent = {
state = listState 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 package cn.super12138.todo.ui.pages.settings.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
@ -43,7 +42,7 @@ fun SettingsRadioDialog(
Column( Column(
verticalArrangement = Arrangement.spacedBy(2.dp), verticalArrangement = Arrangement.spacedBy(2.dp),
modifier = Modifier modifier = Modifier
.clip(MaterialTheme.shapes.extraLarge) .clip(TodoDefaults.SettingsItemRoundedShape)
.selectableGroup() .selectableGroup()
.verticalScroll(rememberScrollState()) .verticalScroll(rememberScrollState())
) { ) {
@ -77,7 +76,7 @@ fun RadioItem(
.fillMaxWidth() .fillMaxWidth()
.height(56.dp) .height(56.dp)
.clip(TodoDefaults.SettingsItemDefaultShape) .clip(TodoDefaults.SettingsItemDefaultShape)
.background(MaterialTheme.colorScheme.surfaceContainerHighest) // .background(MaterialTheme.colorScheme.surfaceContainerHighest)
.selectable( .selectable(
selected = selected, selected = selected,
onClick = { onClick = {

View file

@ -28,6 +28,7 @@ import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.utils.VibrationUtils import cn.super12138.todo.utils.VibrationUtils
import cn.super12138.todo.utils.getPartialRoundedShape import cn.super12138.todo.utils.getPartialRoundedShape
// Leading icon as drawable resource
@Composable @Composable
fun SettingsItem( fun SettingsItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -38,44 +39,20 @@ fun SettingsItem(
bottomRounded: Boolean = false, bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) = SettingsItem(
SettingsItem( leadingIcon = painterResource(leadingIconRes),
leadingIcon = painterResource(leadingIconRes), title = title,
title = title, description = description,
description = description, trailingContent = null,
trailingContent = null, topRounded = topRounded,
topRounded = topRounded, bottomRounded = bottomRounded,
bottomRounded = bottomRounded, enableClick = enableClick,
enableClick = enableClick, onClick = onClick,
onClick = onClick, modifier = modifier
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 @Composable
fun SettingsItem( fun SettingsItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@ -88,29 +65,61 @@ fun SettingsItem(
bottomRounded: Boolean = false, bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) = SettingsItem(
SettingsItem( leadingIcon = {
leadingIcon = { leadingIcon?.let {
leadingIcon?.let { /*Box(
Icon( modifier = Modifier
painter = leadingIcon, .padding(end = TodoDefaults.settingsItemHorizontalPadding)
contentDescription = null, .background(
tint = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.secondaryContainer,
modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding), shape = MaterialShapes.Cookie6Sided.toShape()
) )
} .size(35.dp),
}, contentAlignment = Alignment.Center
title = title, ) {*/
description = description, Icon(
trailingContent = trailingContent, painter = leadingIcon,
background = background, contentDescription = null,
topRounded = topRounded, tint = MaterialTheme.colorScheme.onSurface,
bottomRounded = bottomRounded, modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding)
enableClick = enableClick, )
onClick = onClick, // }
modifier = modifier }
) },
} 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 @Composable
fun SettingsItem( fun SettingsItem(
@ -124,29 +133,28 @@ fun SettingsItem(
bottomRounded: Boolean = false, bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {} onClick: () -> Unit = {}
) { ) = SettingsItem(
SettingsItem( leadingIcon = {
leadingIcon = { leadingIcon?.let {
leadingIcon?.let { Icon(
Icon( imageVector = it,
imageVector = it, contentDescription = null,
contentDescription = null, tint = MaterialTheme.colorScheme.onSurface,
tint = MaterialTheme.colorScheme.onSurface, modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding),
modifier = Modifier.padding(end = TodoDefaults.settingsItemHorizontalPadding), )
) }
} },
}, title = title,
title = title, description = description,
description = description, trailingContent = trailingContent,
trailingContent = trailingContent, background = background,
background = background, topRounded = topRounded,
topRounded = topRounded, bottomRounded = bottomRounded,
bottomRounded = bottomRounded, enableClick = enableClick,
enableClick = enableClick, onClick = onClick,
onClick = onClick, modifier = modifier
modifier = modifier )
)
}
@OptIn(ExperimentalMaterial3ExpressiveApi::class) @OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable @Composable
@ -163,6 +171,57 @@ fun SettingsItem(
bottomRounded: Boolean = false, bottomRounded: Boolean = false,
enableClick: Boolean = true, enableClick: Boolean = true,
onClick: () -> Unit = {}, 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 val view = LocalView.current
Row( Row(
@ -171,47 +230,23 @@ fun SettingsItem(
.wrapContentHeight() .wrapContentHeight()
.clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape)) .clip(shape.getPartialRoundedShape(topRounded, bottomRounded, roundedShape))
.clickable( .clickable(
enabled = enableClick, enabled = enableClick, onClick = {
onClick = {
VibrationUtils.performHapticFeedback(view) VibrationUtils.performHapticFeedback(view)
onClick() onClick()
} })
)
.background(background) .background(background)
.padding( .padding(
horizontal = TodoDefaults.settingsItemHorizontalPadding, horizontal = TodoDefaults.settingsItemHorizontalPadding,
vertical = TodoDefaults.settingsItemVerticalPadding vertical = TodoDefaults.settingsItemVerticalPadding
), ), verticalAlignment = Alignment.CenterVertically
verticalAlignment = Alignment.CenterVertically
) { ) {
leadingIcon?.let { leadingIcon?.let { it() }
it()
}
Column(modifier = Modifier.weight(1f)) { Column(modifier = Modifier.weight(1f)) {
Text( headlineContent?.let { it() }
text = title, supportingContent?.let { it() }
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
)
)
}
} }
trailingContent?.let { trailingContent?.let { it() }
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 # Compose
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-animation = { group = "androidx.compose.animation", name = "animation" } 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 = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-android = { group = "androidx.compose.ui", name = "ui-android" } androidx-ui-android = { group = "androidx.compose.ui", name = "ui-android" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } 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-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } 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 = { 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-runtime = { module = "androidx.navigation3:navigation3-runtime", version.ref = "nav3Core" }
androidx-navigation3-ui = { module = "androidx.navigation3:navigation3-ui", 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 # Konfetti
nl-dionsegijn-konfetti-compose = { group = "nl.dionsegijn", name = "konfetti-compose", 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 # Kotlin
kotlinx-coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "kotlinCoroutines" } 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" } kotlinx-coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "kotlinCoroutines" }