@ -129,36 +129,39 @@ private fun VoiceBubble(message: Message, textColor: Color,
/* Waveform with seek on tap */
if ( wfCount >= 2 ) {
Canvas ( modifier = Modifier . weight ( 1f ) . height ( 28. dp )
. pointerInput ( message . filePath ) {
detectTapGestures { offset ->
if ( message . filePath . isEmpty ( ) ) return @detectTapGestures
val seekFrac = ( offset . x / size . width ) . coerceIn ( 0f , 1f )
val seekMs = ( seekFrac * durMs ) . toInt ( )
playing = true ; paused = false ; elapsedMs = seekMs ; progress = seekFrac
onSeek ( message . filePath , seekMs , onDone )
Box (
modifier = Modifier . weight ( 1f ) . height ( 28. dp )
. pointerInput ( message . filePath ) {
detectTapGestures { offset ->
if ( message . filePath . isEmpty ( ) ) return @detectTapGestures
val seekFrac = ( offset . x / size . width ) . coerceIn ( 0f , 1f )
val seekMs = ( seekFrac * durMs ) . toInt ( )
playing = true ; paused = false ; elapsedMs = seekMs ; progress = seekFrac
onSeek ( message . filePath , seekMs , onDone )
}
}
}
) {
val barW = size . width / wfCount
val ph = size . height
val showCursor = playing && ! paused
val playedIdx = if ( showCursor && progress > 0f ) ( progress * wfCount ) . toInt ( ) else 0
for ( i in 0 until wfCount ) {
val level = wf [ i ] . coerceIn ( 0f , 1f )
val barH = ( 2f + level * ( ph - 2f ) ) . coerceAtMost ( ph )
val x = i . toFloat ( ) * barW
val y = ph - barH
val played = showCursor && i < playedIdx
drawRect (
color = when { played -> Color . White ; else -> Color ( 0xFFCCCCD4 ) . copy ( alpha = 0.8f ) } ,
topLeft = Offset ( x , y ) ,
size = Size ( barW * 0.75f , barH )
)
}
if ( showCursor && progress > 0f && progress < 1f ) {
val cursorX = progress * size . width
drawLine ( Color ( 0xFFFFD600 ) , Offset ( cursorX , 0f ) , Offset ( cursorX , ph ) , strokeWidth = 2.5f )
Canvas ( modifier = Modifier . fillMaxSize ( ) ) {
val barW = size . width / wfCount
val ph = size . height
val showCursor = playing || paused
val playedIdx = if ( showCursor && progress > 0f ) ( progress * wfCount ) . toInt ( ) else 0
for ( i in 0 until wfCount ) {
val level = wf [ i ] . coerceIn ( 0f , 1f )
val barH = ( 2f + level * ( ph - 2f ) ) . coerceAtMost ( ph )
val x = i . toFloat ( ) * barW
val y = ph - barH
val played = showCursor && i < playedIdx
drawRect (
color = when { played -> Color . White ; else -> Color ( 0xFFCCCCD4 ) . copy ( alpha = 0.8f ) } ,
topLeft = Offset ( x , y ) ,
size = Size ( barW * 0.75f , barH )
)
}
if ( showCursor && progress > 0f && progress < 1f ) {
val cursorX = progress * size . width
drawLine ( Color ( 0xFFFFD600 ) , Offset ( cursorX , 0f ) , Offset ( cursorX , ph ) , strokeWidth = 2.5f )
}
}
}
}
@ -173,7 +176,7 @@ private fun VoiceBubble(message: Message, textColor: Color,
verticalAlignment = Alignment . Bottom
) {
Text (
if ( playing && ! paused ) " ${formatDurationMs(elapsedMs)} / ${formatDurationMs(durMs)} "
if ( playing || paused ) " ${formatDurationMs(elapsedMs)} / ${formatDurationMs(durMs)} "
else formatDurationMs ( durMs ) ,
fontSize = 12. sp ,
color = textColor . copy ( alpha = 0.55f )