Add TextCard component

This commit is contained in:
vfsfitvnm 2022-06-29 21:33:35 +02:00
parent 53190a39f7
commit fd41e78625
4 changed files with 137 additions and 94 deletions

View file

@ -0,0 +1,110 @@
package it.vfsfitvnm.vimusic.ui.components.themed
import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
import it.vfsfitvnm.vimusic.utils.align
import it.vfsfitvnm.vimusic.utils.secondary
import it.vfsfitvnm.vimusic.utils.semiBold
@Composable
fun TextCard(
modifier: Modifier = Modifier,
@DrawableRes icon: Int? = null,
iconColor: ColorFilter? = null,
onClick: (() -> Unit)? = null,
content: @Composable TextCardScope.() -> Unit,
) {
val colorPalette = LocalColorPalette.current
Column(
modifier = modifier
.padding(horizontal = 16.dp, vertical = 16.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = true),
enabled = onClick != null,
onClick = onClick ?: {}
)
.background(colorPalette.lightBackground)
.padding(horizontal = 16.dp, vertical = 16.dp)
) {
icon?.let {
Image(
painter = painterResource(icon),
contentDescription = null,
colorFilter = iconColor ?: ColorFilter.tint(colorPalette.red),
modifier = Modifier
.padding(bottom = 16.dp)
.size(24.dp)
)
}
(icon?.let { IconTextCardScopeImpl } ?: TextCardScopeImpl).content()
}
}
interface TextCardScope {
@Composable
fun Title(text: String)
@Composable
fun Text(text: String)
}
private object TextCardScopeImpl : TextCardScope {
@Composable
override fun Title(text: String) {
BasicText(
text = text,
style = LocalTypography.current.xxs.semiBold,
)
}
@Composable
override fun Text(text: String) {
BasicText(
text = text,
style = LocalTypography.current.xxs.secondary.align(TextAlign.Justify),
)
}
}
private object IconTextCardScopeImpl : TextCardScope {
@Composable
override fun Title(text: String) {
BasicText(
text = text,
style = LocalTypography.current.xxs.semiBold,
modifier = Modifier
.padding(horizontal = 16.dp)
)
}
@Composable
override fun Text(text: String) {
BasicText(
text = text,
style = LocalTypography.current.xxs.secondary,
modifier = Modifier
.padding(horizontal = 16.dp)
)
}
}

View file

@ -4,14 +4,12 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.BasicText
import androidx.compose.material.ripple.rememberRipple
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -24,7 +22,6 @@ import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex import androidx.compose.ui.zIndex
import coil.compose.AsyncImage import coil.compose.AsyncImage
@ -38,6 +35,7 @@ import it.vfsfitvnm.vimusic.models.DetailedSong
import it.vfsfitvnm.vimusic.query import it.vfsfitvnm.vimusic.query
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu import it.vfsfitvnm.vimusic.ui.components.themed.InHistoryMediaItemMenu
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder import it.vfsfitvnm.vimusic.ui.components.themed.TextPlaceholder
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
@ -154,7 +152,11 @@ fun ArtistScreen(
.clickable { .clickable {
query { query {
runBlocking { runBlocking {
Database.artist(browseId).first()?.copy(shufflePlaylistId = null)?.let(Database::update) Database
.artist(browseId)
.first()
?.copy(shufflePlaylistId = null)
?.let(Database::update)
} }
} }
} }
@ -292,22 +294,9 @@ fun ArtistScreen(
artistResult?.getOrNull()?.info?.let { description -> artistResult?.getOrNull()?.info?.let { description ->
item { item {
Column( TextCard {
modifier = Modifier Title(text = "Information")
.padding(top = 32.dp) Text(text = description)
.padding(horizontal = 16.dp, vertical = 16.dp)
.background(colorPalette.lightBackground)
.padding(horizontal = 16.dp, vertical = 16.dp)
) {
BasicText(
text = "Information",
style = typography.xxs.semiBold
)
BasicText(
text = description,
style = typography.xxs.secondary.align(TextAlign.Justify)
)
} }
} }
} }
@ -321,7 +310,6 @@ private fun LoadingOrError(
errorMessage: String? = null, errorMessage: String? = null,
onRetry: (() -> Unit)? = null onRetry: (() -> Unit)? = null
) { ) {
val typography = LocalTypography.current
val colorPalette = LocalColorPalette.current val colorPalette = LocalColorPalette.current
Box { Box {
@ -353,41 +341,14 @@ private fun LoadingOrError(
} }
errorMessage?.let { errorMessage?.let {
Column( TextCard(
icon = R.drawable.alert_circle,
onClick = onRetry,
modifier = Modifier modifier = Modifier
.align(Alignment.Center) .align(Alignment.Center)
.padding(horizontal = 16.dp, vertical = 16.dp)
.clickable(
interactionSource = remember { MutableInteractionSource() },
indication = rememberRipple(bounded = true),
enabled = onRetry != null,
onClick = onRetry ?: {}
)
.background(colorPalette.lightBackground)
.padding(horizontal = 16.dp, vertical = 16.dp)
) { ) {
Image( Title(text = onRetry?.let { "Tap to retry" } ?: "Error")
painter = painterResource(R.drawable.alert_circle), Text(text = "An error has occurred:\n$errorMessage")
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.red),
modifier = Modifier
.padding(bottom = 16.dp)
.size(24.dp)
)
BasicText(
text = onRetry?.let { "Tap to retry" } ?: "Error",
style = typography.xxs.semiBold,
modifier = Modifier
.padding(horizontal = 16.dp)
)
BasicText(
text = "An error has occurred:\n$errorMessage",
style = typography.xxs.secondary,
modifier = Modifier
.padding(horizontal = 16.dp)
)
} }
} }
} }

View file

@ -25,6 +25,7 @@ import it.vfsfitvnm.vimusic.*
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog import it.vfsfitvnm.vimusic.ui.components.themed.ConfirmationDialog
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen import it.vfsfitvnm.vimusic.ui.screens.ArtistScreen
import it.vfsfitvnm.vimusic.ui.screens.PlaylistOrAlbumScreen import it.vfsfitvnm.vimusic.ui.screens.PlaylistOrAlbumScreen
import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute import it.vfsfitvnm.vimusic.ui.screens.rememberArtistRoute
@ -194,7 +195,6 @@ fun BackupAndRestoreScreen() {
) )
} }
Column( Column(
verticalArrangement = Arrangement.spacedBy(8.dp), verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
@ -231,53 +231,19 @@ fun BackupAndRestoreScreen() {
} }
} }
Column( TextCard(
modifier = Modifier icon = R.drawable.alert_circle,
.padding(horizontal = 16.dp, vertical = 16.dp)
.background(colorPalette.lightBackground)
.padding(horizontal = 16.dp, vertical = 16.dp)
) { ) {
Image( Title(text = "Backup")
painter = painterResource(R.drawable.alert_circle), Text(text = "The backup consists in exporting the application database to your device storage.\nThis means playlists, song history, favorites songs will exported.\nThis operation excludes personal preferences such as the theme mode and everything you can set in the Settings page.")
contentDescription = null,
colorFilter = ColorFilter.tint(colorPalette.red),
modifier = Modifier
.padding(bottom = 16.dp)
.size(24.dp)
)
//
BasicText(
text = "Backup",
style = typography.xxs.semiBold,
modifier = Modifier
.padding(horizontal = 16.dp)
)
BasicText(
text = "The backup consists in exporting the application database to your device storage.\nThis means playlists, song history, favorites songs will exported.\nThis operation excludes personal preferences such as the theme mode and everything you can set in the Settings page.",
style = typography.xxs.secondary,
modifier = Modifier
.padding(horizontal = 16.dp)
)
Spacer( Spacer(
modifier = Modifier modifier = Modifier
.height(32.dp) .height(32.dp)
) )
BasicText( Title(text = "Restore")
text = "Restore", Text(text = "The restore replaces the existing application database with the selected - previously exported - one.\nThis means every currently existing data will be wiped: THE TWO DATABASES WON'T BE MERGED.\nIt is recommended to restore the database immediately after the application is installed on a new device.")
style = typography.xxs.semiBold,
modifier = Modifier
.padding(horizontal = 16.dp)
)
BasicText(
text = "The restore replaces the existing application database with the selected - previously exported - one.\nThis means every currently existing data will be wiped: THE TWO DATABASES WON'T BE MERGED.\nIt is recommended to restore the database immediately after the application is installed on a new device.",
style = typography.xxs.secondary,
modifier = Modifier
.padding(horizontal = 16.dp)
)
} }
} }
} }

View file

@ -19,6 +19,7 @@ import it.vfsfitvnm.vimusic.LocalPlayerServiceBinder
import it.vfsfitvnm.vimusic.R import it.vfsfitvnm.vimusic.R
import it.vfsfitvnm.vimusic.ui.components.SeekBar import it.vfsfitvnm.vimusic.ui.components.SeekBar
import it.vfsfitvnm.vimusic.ui.components.TopAppBar import it.vfsfitvnm.vimusic.ui.components.TopAppBar
import it.vfsfitvnm.vimusic.ui.components.themed.TextCard
import it.vfsfitvnm.vimusic.ui.screens.* import it.vfsfitvnm.vimusic.ui.screens.*
import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette import it.vfsfitvnm.vimusic.ui.styling.LocalColorPalette
import it.vfsfitvnm.vimusic.ui.styling.LocalTypography import it.vfsfitvnm.vimusic.ui.styling.LocalTypography
@ -224,6 +225,11 @@ fun OtherSettingsScreen() {
text = "${Formatter.formatShortFileSize(context, diskCacheSize)} (${diskCacheSize * 100 / preferences.exoPlayerDiskCacheMaxSizeBytes.coerceAtLeast(1)}%)", text = "${Formatter.formatShortFileSize(context, diskCacheSize)} (${diskCacheSize * 100 / preferences.exoPlayerDiskCacheMaxSizeBytes.coerceAtLeast(1)}%)",
) )
} }
TextCard(icon = R.drawable.alert_circle) {
Title(text = "Cache strategy")
Text(text = "The cache follows the LRU (Least Recently Used) strategy: when it runs out of space, the resources that haven't been accessed for the longest time are cleared to accommodate the new resource.")
}
} }
} }
} }