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:
committed by
GitHub
parent
ae424ffe6a
commit
e762584a37
1
changelog.d/2156.bugfix
Normal file
1
changelog.d/2156.bugfix
Normal file
@@ -0,0 +1 @@
|
||||
Improve rendering of voice messages in the timeline in large displays
|
||||
@@ -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)) },
|
||||
)
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user