Improve rendering of voice messages in the timeline in large displays (#2199)

* Improve rendering of voice messages in the timeline in large displays

---------

Co-authored-by: ElementBot <benoitm+elementbot@element.io>
This commit is contained in:
Jorge Martin Espinosa
2024-01-09 18:07:23 +01:00
committed by GitHub
parent ae424ffe6a
commit e762584a37
31 changed files with 80 additions and 76 deletions

1
changelog.d/2156.bugfix Normal file
View File

@@ -0,0 +1 @@
Improve rendering of voice messages in the timeline in large displays

View File

@@ -110,9 +110,7 @@ fun TimelineItemVoiceView(
showCursor = state.showCursor,
playbackProgress = state.progress,
waveform = content.waveform,
modifier = Modifier
.height(34.dp)
.weight(1f),
modifier = Modifier.height(34.dp),
seekEnabled = !context.isScreenReaderEnabled(),
onSeek = { state.eventSink(VoiceMessageEvents.Seek(it)) },
)

View File

@@ -23,23 +23,22 @@ import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.drawscope.DrawScope
import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import kotlinx.collections.immutable.ImmutableList
import kotlin.math.max
fun DrawScope.drawWaveform(
waveformData: ImmutableList<Float>,
canvasSize: DpSize,
canvasSizePx: Size,
brush: Brush,
minimumGraphAmplitude: Float = 2F,
lineWidth: Dp = 2.dp,
linePadding: Dp = 2.dp,
) {
val centerY = canvasSize.height.toPx() / 2
val centerY = canvasSizePx.height / 2
val cornerRadius = lineWidth / 2
waveformData.forEachIndexed { index, amplitude ->
val drawingAmplitude = max(minimumGraphAmplitude, amplitude * (canvasSize.height.toPx() - 2))
val drawingAmplitude = max(minimumGraphAmplitude, amplitude * (canvasSizePx.height - 2))
drawRoundRect(
brush = brush,
topLeft = Offset(

View File

@@ -40,12 +40,13 @@ import androidx.compose.ui.graphics.drawscope.Fill
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.RequestDisallowInterceptTouchEvent
import androidx.compose.ui.input.pointer.pointerInteropFilter
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpSize
import androidx.compose.ui.unit.dp
import io.element.android.compound.theme.ElementTheme
import io.element.android.libraries.designsystem.preview.ElementPreview
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
import io.element.android.compound.theme.ElementTheme
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.persistentListOf
import kotlinx.collections.immutable.toPersistentList
@@ -58,7 +59,7 @@ private const val DEFAULT_GRAPHICS_LAYER_ALPHA: Float = 0.99F
*
* @param playbackProgress The current playback progress, between 0 and 1.
* @param showCursor Whether to show the cursor or not.
* @param waveform The waveform to display. Use [FakeWaveformFactory] to generate a fake waveform.
* @param waveform The waveform to display. Use [createFakeWaveform] to generate a fake waveform.
* @param onSeek Callback when the user seeks the waveform. Called with a value between 0 and 1.
* @param modifier The modifier to be applied to the view.
* @param seekEnabled Whether the user can seek the waveform or not.
@@ -103,6 +104,11 @@ fun WaveformPlaybackView(
}
}
val density = LocalDensity.current
val waveformWidthPx by remember {
derivedStateOf { with(density) { normalizedWaveformData.size * (lineWidth + linePadding).roundToPx().toFloat() } }
}
val requestDisallowInterceptTouchEvent = remember { RequestDisallowInterceptTouchEvent() }
Canvas(
modifier = Modifier
@@ -110,19 +116,18 @@ fun WaveformPlaybackView(
.graphicsLayer(alpha = DEFAULT_GRAPHICS_LAYER_ALPHA)
.let {
if (!seekEnabled) return@let it
it.pointerInteropFilter(requestDisallowInterceptTouchEvent = requestDisallowInterceptTouchEvent) { e ->
return@pointerInteropFilter when (e.action) {
MotionEvent.ACTION_DOWN -> {
if (e.x in 0F..canvasSizePx.width) {
if (e.x in 0F..waveformWidthPx) {
requestDisallowInterceptTouchEvent.invoke(true)
seekProgress.value = e.x / canvasSizePx.width
seekProgress.value = e.x / waveformWidthPx
true
} else false
}
MotionEvent.ACTION_MOVE -> {
if (e.x in 0F..canvasSizePx.width) {
seekProgress.value = e.x / canvasSizePx.width
if (e.x in 0F..waveformWidthPx) {
seekProgress.value = e.x / waveformWidthPx
}
true
}
@@ -140,11 +145,11 @@ fun WaveformPlaybackView(
) {
canvasSize = size.toDpSize()
canvasSizePx = size
val centerY = canvasSize.height.toPx() / 2
val cornerRadius = lineWidth / 2
// Calculate the size of the waveform by summing the width of all the lines and paddings
drawWaveform(
waveformData = normalizedWaveformData,
canvasSize = canvasSize,
canvasSizePx = canvasSizePx,
brush = brush,
lineWidth = lineWidth,
linePadding = linePadding
@@ -152,8 +157,8 @@ fun WaveformPlaybackView(
drawRect(
brush = progressBrush,
size = Size(
width = progressAnimated.value * canvasSize.width.toPx(),
height = canvasSize.height.toPx()
width = progressAnimated.value * waveformWidthPx,
height = canvasSizePx.height
),
blendMode = BlendMode.SrcAtop
)
@@ -161,12 +166,12 @@ fun WaveformPlaybackView(
drawRoundRect(
brush = cursorBrush,
topLeft = Offset(
x = progressAnimated.value * canvasSize.width.toPx(),
y = centerY - (canvasSize.height.toPx() - 2) / 2
x = progressAnimated.value * waveformWidthPx,
y = 1f
),
size = Size(
width = lineWidth.toPx(),
height = canvasSize.height.toPx() - 2
height = canvasSizePx.height - 2
),
cornerRadius = CornerRadius(cornerRadius.toPx(), cornerRadius.toPx()),
style = Fill

View File

@@ -31,6 +31,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Size
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.graphicsLayer
@@ -66,7 +67,6 @@ fun LiveWaveformView(
}
}
Box(contentAlignment = Alignment.CenterEnd,
modifier = modifier
.fillMaxWidth()
@@ -79,11 +79,12 @@ fun LiveWaveformView(
.graphicsLayer(alpha = DEFAULT_GRAPHICS_LAYER_ALPHA)
.then(modifier)
) {
canvasSize = DpSize(Dp(min(waveformWidth, parentWidth.toFloat())), size.height.toDp())
val width = min(waveformWidth, parentWidth.toFloat())
canvasSize = DpSize(width.dp, size.height.toDp())
val countThatFitsWidth = (parentWidth.toFloat() / (lineWidth.toPx() + linePadding.toPx())).toInt()
drawWaveform(
waveformData = levels.takeLast(countThatFitsWidth).toPersistentList(),
canvasSize = canvasSize,
canvasSizePx = Size(canvasSize.width.toPx(), size.height),
brush = brush,
lineWidth = lineWidth,
linePadding = linePadding,