package com.suno.android.common_ui.components.bottom_bar import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.fillMaxHeight import androidx.compose.foundation.layout.fillMaxWidth 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.shape.RoundedCornerShape import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable 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.graphics.painter.ColorPainter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import androidx.constraintlayout.compose.ConstraintLayout import androidx.constraintlayout.compose.Dimension import coil3.compose.AsyncImage import com.suno.android.common_core_utils.model.Url import com.suno.android.common_ui.R import com.suno.android.common_ui.theme.ExtendedTheme import com.suno.android.common_ui.theme.SunoTheme import kotlin.time.Duration import kotlin.time.DurationUnit import kotlin.time.toDuration import com.suno.android.common_res.R as CommonResR @Composable fun BottomMediaPlayerCard( modifier: Modifier = Modifier, titleText: String? = null, artistName: String? = null, clipImageUrl: Url? = null, isPlaying: Boolean = false, clipProgress: Duration? = null, clipLength: Duration? = null, onPlayPauseClick: () -> Unit = {}, onNextClick: () -> Unit = {}, onCardSurfaceClick: () -> Unit = {}, ) { Column( modifier = modifier .padding(bottom = 4.dp) .fillMaxWidth() .clip(RoundedCornerShape(12.dp)) .clickable { onCardSurfaceClick.invoke() } .background(color = ExtendedTheme.colors.backgroundSecondary) .padding( top = 8.dp, start = 4.dp, end = 4.dp, ), // Ensure padding is within the height constraint ) { ConstraintLayout( modifier = Modifier .fillMaxWidth() .padding(horizontal = 12.dp) .padding(bottom = 4.dp), ) { val (image, title, artist, pauseIcon, nextIcon) = createRefs() AsyncImage( modifier = Modifier .height(56.dp) // Control the height of the image .width(42.dp) .clip(RoundedCornerShape(6.dp)) .constrainAs(image) { start.linkTo(parent.start, margin = 4.dp) centerVerticallyTo(parent) }, model = clipImageUrl, contentDescription = null, placeholder = ColorPainter(color = Color.Gray), contentScale = ContentScale.Crop, ) Text( style = ExtendedTheme.typography.body, color = ExtendedTheme.colors.textPrimary, text = if (titleText.isNullOrEmpty()) stringResource(CommonResR.string.untitled) else titleText, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier.constrainAs(title) { start.linkTo(image.end, margin = 16.dp) end.linkTo(pauseIcon.start, margin = 16.dp) width = Dimension.fillToConstraints top.linkTo(parent.top, margin = 8.dp) }, ) Text( text = artistName ?: "", style = ExtendedTheme.typography.body, color = ExtendedTheme.colors.textPrimary, maxLines = 1, overflow = TextOverflow.Ellipsis, modifier = Modifier .constrainAs(artist) { start.linkTo(image.end, margin = 16.dp) end.linkTo(pauseIcon.start, margin = 16.dp) width = Dimension.fillToConstraints top.linkTo(title.bottom, margin = 4.dp) bottom.linkTo(parent.bottom, margin = 8.dp) } .alpha(0.5f), ) IconButton( modifier = Modifier .size(24.dp) .constrainAs(pauseIcon) { end.linkTo(nextIcon.start, margin = 16.dp) centerVerticallyTo(parent) }, onClick = { onPlayPauseClick.invoke() }, ) { if (isPlaying) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.pause), tint = ExtendedTheme.colors.textPrimary, contentDescription = "Pause", ) } else { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.play), tint = ExtendedTheme.colors.textPrimary, contentDescription = "Play", ) } } IconButton( modifier = Modifier .size(24.dp) .constrainAs(nextIcon) { end.linkTo(parent.end, margin = 16.dp) centerVerticallyTo(parent) }, onClick = { onNextClick.invoke() }, ) { Icon( imageVector = ImageVector.vectorResource(id = R.drawable.forward), tint = ExtendedTheme.colors.textPrimary, contentDescription = "Next", ) } } clipProgress?.let { progress -> clipLength?.let { length -> val progressPercent = progress.inWholeMilliseconds.toFloat() / length.inWholeMilliseconds.toFloat() Box( modifier = Modifier .height(4.dp) .fillMaxWidth() .background( color = ExtendedTheme.colors.backgroundTertiary, shape = MaterialTheme.shapes.medium, ), ) { Box( modifier = Modifier .fillMaxHeight() .fillMaxWidth(progressPercent) .background( color = ExtendedTheme.colors.textSecondary, shape = MaterialTheme.shapes.medium, ), ) } } } } } @Preview @Composable private fun BottomMediaPlayerCard_Preview() { SunoTheme { BottomMediaPlayerCard( modifier = Modifier.padding(4.dp), titleText = null, artistName = "Artist Name", clipImageUrl = Url("https://example.com/your-image-url.jpg"), // Add an image URL for preview clipLength = 2.toDuration(DurationUnit.MINUTES), clipProgress = 45.toDuration(DurationUnit.SECONDS), ) } } @Preview(uiMode = android.content.res.Configuration.UI_MODE_NIGHT_YES) @Composable private fun BottomMediaPlayerCard_Preview_Dark() { SunoTheme { BottomMediaPlayerCard( modifier = Modifier.padding(4.dp), titleText = null, artistName = "Artist Name", clipImageUrl = Url("https://example.com/your-image-url.jpg"), // Add an image URL for preview clipLength = 2.toDuration(DurationUnit.MINUTES), clipProgress = 45.toDuration(DurationUnit.SECONDS), ) } }