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