Remove WaveformProgressIndicator and waveform library and lint.
-Remove WaveformProgressIndicator - Remove waveform library - lint.
This commit is contained in:
parent
4b403ae57c
commit
5a646a8362
7 changed files with 57 additions and 136 deletions
|
|
@ -65,7 +65,6 @@ dependencies {
|
||||||
implementation(libs.vanniktech.blurhash)
|
implementation(libs.vanniktech.blurhash)
|
||||||
implementation(libs.telephoto.zoomableimage)
|
implementation(libs.telephoto.zoomableimage)
|
||||||
implementation(libs.matrix.emojibase.bindings)
|
implementation(libs.matrix.emojibase.bindings)
|
||||||
implementation(libs.audiowaveform)
|
|
||||||
|
|
||||||
testImplementation(libs.test.junit)
|
testImplementation(libs.test.junit)
|
||||||
testImplementation(libs.coroutines.test)
|
testImplementation(libs.coroutines.test)
|
||||||
|
|
|
||||||
|
|
@ -46,7 +46,6 @@ import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMes
|
||||||
import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageStateProvider
|
import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageStateProvider
|
||||||
import io.element.android.features.messages.impl.voicemessages.timeline.Waveform
|
import io.element.android.features.messages.impl.voicemessages.timeline.Waveform
|
||||||
import io.element.android.features.messages.impl.voicemessages.timeline.WaveformPlaybackView
|
import io.element.android.features.messages.impl.voicemessages.timeline.WaveformPlaybackView
|
||||||
import io.element.android.features.messages.impl.voicemessages.timeline.WaveformProgressIndicator
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
import io.element.android.libraries.designsystem.theme.components.CircularProgressIndicator
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,50 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023 New Vector Ltd
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.element.android.features.messages.impl.voicemessages.timeline
|
||||||
|
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
|
data class Waveform (
|
||||||
|
val data: ImmutableList<Int>
|
||||||
|
) {
|
||||||
|
companion object {
|
||||||
|
private val dataRange = 0..1024
|
||||||
|
}
|
||||||
|
|
||||||
|
fun normalisedData(maxSamplesCount: Int): ImmutableList<Float> {
|
||||||
|
if(maxSamplesCount <= 0) {
|
||||||
|
return persistentListOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter the data to keep only the expected number of samples
|
||||||
|
val result = if (data.size > maxSamplesCount) {
|
||||||
|
(0..<maxSamplesCount)
|
||||||
|
.map { index ->
|
||||||
|
val targetIndex = (index.toDouble() * (data.count().toDouble() / maxSamplesCount.toDouble())).roundToInt()
|
||||||
|
data[targetIndex]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data
|
||||||
|
}
|
||||||
|
|
||||||
|
// Normalize the sample in the allowed range
|
||||||
|
return result.map { it.toFloat() / dataRange.last.toFloat() }.toPersistentList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -45,39 +45,10 @@ import androidx.compose.ui.unit.dp
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
import io.element.android.libraries.designsystem.preview.ElementPreview
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
import io.element.android.libraries.theme.ElementTheme
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
import kotlinx.collections.immutable.toPersistentList
|
||||||
import kotlin.math.max
|
import kotlin.math.max
|
||||||
import kotlin.math.roundToInt
|
|
||||||
|
|
||||||
data class Waveform (
|
|
||||||
val data: ImmutableList<Int>
|
|
||||||
) {
|
|
||||||
companion object {
|
|
||||||
private val dataRange = 0..1024
|
|
||||||
}
|
|
||||||
|
|
||||||
fun normalisedData(maxSamplesCount: Int): ImmutableList<Float> {
|
|
||||||
if(maxSamplesCount <= 0) {
|
|
||||||
return persistentListOf()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Filter the data to keep only the expected number of samples
|
|
||||||
val result = if (data.size > maxSamplesCount) {
|
|
||||||
(0..<maxSamplesCount)
|
|
||||||
.map { index ->
|
|
||||||
val targetIndex = (index.toDouble() * (data.count().toDouble() / maxSamplesCount.toDouble())).roundToInt()
|
|
||||||
data[targetIndex]
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
data
|
|
||||||
}
|
|
||||||
|
|
||||||
// Normalize the sample in the allowed range
|
|
||||||
return result.map { it.toFloat() / dataRange.last.toFloat() }.toPersistentList()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
private const val DEFAULT_GRAPHICS_LAYER_ALPHA: Float = 0.99F
|
private const val DEFAULT_GRAPHICS_LAYER_ALPHA: Float = 0.99F
|
||||||
@OptIn(ExperimentalComposeUiApi::class)
|
@OptIn(ExperimentalComposeUiApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -94,7 +65,7 @@ fun WaveformPlaybackView(
|
||||||
linePadding: Dp = 2.dp,
|
linePadding: Dp = 2.dp,
|
||||||
minimumGraphAmplitude: Float = 2F,
|
minimumGraphAmplitude: Float = 2F,
|
||||||
) {
|
) {
|
||||||
var seekProgress = remember { mutableStateOf<Float?>(null) }
|
val seekProgress = remember { mutableStateOf<Float?>(null) }
|
||||||
var canvasSize by remember { mutableStateOf(DpSize(0.dp, 0.dp)) }
|
var canvasSize by remember { mutableStateOf(DpSize(0.dp, 0.dp)) }
|
||||||
var canvasSizePx by remember { mutableStateOf(Size(0f, 0f)) }
|
var canvasSizePx by remember { mutableStateOf(Size(0f, 0f)) }
|
||||||
val progress by remember(playbackProgress, seekProgress.value) {
|
val progress by remember(playbackProgress, seekProgress.value) {
|
||||||
|
|
@ -105,7 +76,7 @@ fun WaveformPlaybackView(
|
||||||
val progressAnimated = animateFloatAsState(targetValue = progress, label = "progressAnimation")
|
val progressAnimated = animateFloatAsState(targetValue = progress, label = "progressAnimation")
|
||||||
val amplitudeDisplayCount by remember(canvasSize) {
|
val amplitudeDisplayCount by remember(canvasSize) {
|
||||||
derivedStateOf {
|
derivedStateOf {
|
||||||
((canvasSize.width.value) / (lineWidth.value + linePadding.value)).toInt()
|
(canvasSize.width.value / (lineWidth.value + linePadding.value)).toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val normalizedWaveformData by remember(amplitudeDisplayCount) {
|
val normalizedWaveformData by remember(amplitudeDisplayCount) {
|
||||||
|
|
@ -124,13 +95,13 @@ fun WaveformPlaybackView(
|
||||||
MotionEvent.ACTION_DOWN -> {
|
MotionEvent.ACTION_DOWN -> {
|
||||||
if (it.x in 0F..canvasSizePx.width) {
|
if (it.x in 0F..canvasSizePx.width) {
|
||||||
requestDisallowInterceptTouchEvent.invoke(true)
|
requestDisallowInterceptTouchEvent.invoke(true)
|
||||||
seekProgress.value = (it.x / canvasSizePx.width)
|
seekProgress.value = it.x / canvasSizePx.width
|
||||||
true
|
true
|
||||||
} else false
|
} else false
|
||||||
}
|
}
|
||||||
MotionEvent.ACTION_MOVE -> {
|
MotionEvent.ACTION_MOVE -> {
|
||||||
if (it.x in 0F..canvasSizePx.width) {
|
if (it.x in 0F..canvasSizePx.width) {
|
||||||
seekProgress.value = (it.x / canvasSizePx.width)
|
seekProgress.value = it.x / canvasSizePx.width
|
||||||
}
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
@ -155,7 +126,7 @@ fun WaveformPlaybackView(
|
||||||
brush = brush,
|
brush = brush,
|
||||||
topLeft = Offset(
|
topLeft = Offset(
|
||||||
x = index * (linePadding + lineWidth).toPx(),
|
x = index * (linePadding + lineWidth).toPx(),
|
||||||
y = centerY - (drawingAmplitude / 2)
|
y = centerY - drawingAmplitude / 2
|
||||||
),
|
),
|
||||||
size = Size(
|
size = Size(
|
||||||
width = lineWidth.toPx(),
|
width = lineWidth.toPx(),
|
||||||
|
|
@ -168,7 +139,7 @@ fun WaveformPlaybackView(
|
||||||
drawRect(
|
drawRect(
|
||||||
brush = progressBrush,
|
brush = progressBrush,
|
||||||
size = Size(
|
size = Size(
|
||||||
width = (progressAnimated.value) * canvasSize.width.toPx(),
|
width = progressAnimated.value * canvasSize.width.toPx(),
|
||||||
height = canvasSize.height.toPx()
|
height = canvasSize.height.toPx()
|
||||||
),
|
),
|
||||||
blendMode = BlendMode.SrcAtop
|
blendMode = BlendMode.SrcAtop
|
||||||
|
|
@ -178,7 +149,7 @@ fun WaveformPlaybackView(
|
||||||
brush = cursorBrush,
|
brush = cursorBrush,
|
||||||
topLeft = Offset(
|
topLeft = Offset(
|
||||||
x = progressAnimated.value * canvasSize.width.toPx(),
|
x = progressAnimated.value * canvasSize.width.toPx(),
|
||||||
y = centerY - ((canvasSize.height.toPx() - 2) / 2)
|
y = centerY - (canvasSize.height.toPx() - 2) / 2
|
||||||
),
|
),
|
||||||
size = Size(
|
size = Size(
|
||||||
width = lineWidth.toPx(),
|
width = lineWidth.toPx(),
|
||||||
|
|
|
||||||
|
|
@ -1,96 +0,0 @@
|
||||||
/*
|
|
||||||
* Copyright (c) 2023 New Vector Ltd
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.element.android.features.messages.impl.voicemessages.timeline
|
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.ui.Modifier
|
|
||||||
import androidx.compose.ui.graphics.SolidColor
|
|
||||||
import androidx.compose.ui.unit.dp
|
|
||||||
import com.linc.audiowaveform.AudioWaveform
|
|
||||||
import io.element.android.libraries.designsystem.preview.ElementPreview
|
|
||||||
import io.element.android.libraries.designsystem.preview.PreviewsDayNight
|
|
||||||
import io.element.android.libraries.theme.ElementTheme
|
|
||||||
import kotlinx.collections.immutable.ImmutableList
|
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
|
||||||
import kotlinx.collections.immutable.toPersistentList
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun WaveformProgressIndicator(
|
|
||||||
progress: Float,
|
|
||||||
amplitudes: ImmutableList<Int>,
|
|
||||||
modifier: Modifier = Modifier,
|
|
||||||
onSeek: (progress: Float) -> Unit = {},
|
|
||||||
) {
|
|
||||||
var seekProgress: Float? by remember { mutableStateOf(null) }
|
|
||||||
val scaledAmplitudes = remember(amplitudes) { amplitudes.scaleAmplitudes() }
|
|
||||||
AudioWaveform(
|
|
||||||
modifier = modifier,
|
|
||||||
waveformBrush = SolidColor(ElementTheme.colors.iconQuaternary),
|
|
||||||
progressBrush = SolidColor(ElementTheme.colors.iconSecondary),
|
|
||||||
onProgressChangeFinished = {
|
|
||||||
// This is to send just one onSeek callback after the user has finished seeking.
|
|
||||||
// Otherwise the AudioWaveform library would send multiple callbacks while the user is seeking.
|
|
||||||
val p = seekProgress!!
|
|
||||||
seekProgress = null
|
|
||||||
onSeek(p)
|
|
||||||
},
|
|
||||||
spikeWidth = 1.6.dp,
|
|
||||||
spikeRadius = 0.8.dp,
|
|
||||||
spikePadding = 3.dp,
|
|
||||||
progress = seekProgress ?: progress,
|
|
||||||
amplitudes = scaledAmplitudes,
|
|
||||||
onProgressChange = { seekProgress = it },
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
@PreviewsDayNight
|
|
||||||
@Composable
|
|
||||||
internal fun WaveformProgressIndicatorPreview() = ElementPreview {
|
|
||||||
Column {
|
|
||||||
WaveformProgressIndicator(
|
|
||||||
progress = 0.5f,
|
|
||||||
amplitudes = persistentListOf(),
|
|
||||||
)
|
|
||||||
WaveformProgressIndicator(
|
|
||||||
progress = 0.5f,
|
|
||||||
amplitudes = persistentListOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0),
|
|
||||||
)
|
|
||||||
WaveformProgressIndicator(
|
|
||||||
progress = 0.5f,
|
|
||||||
amplitudes = List(1024) { it }.toPersistentList()
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Scale amplitudes to fit in the waveform view.
|
|
||||||
*
|
|
||||||
* It seems amplitudes > 128 are clipped by the waveform library.
|
|
||||||
* Workaround for https://github.com/lincollincol/compose-audiowaveform/issues/22
|
|
||||||
*
|
|
||||||
* TODO Voice messages: Remove this workaround when the waveform library is fixed.
|
|
||||||
*/
|
|
||||||
private fun ImmutableList<Int>.scaleAmplitudes(): List<Int> {
|
|
||||||
val maxAmplitude = if (isEmpty()) 1 else maxOf { it }
|
|
||||||
val scalingFactor = 128 / maxAmplitude.toFloat()
|
|
||||||
return map { (it * scalingFactor).toInt() }
|
|
||||||
}
|
|
||||||
|
|
@ -166,7 +166,6 @@ maplibre = "org.maplibre.gl:android-sdk:10.2.0"
|
||||||
maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.1"
|
maplibre_ktx = "org.maplibre.gl:android-sdk-ktx-v7:2.0.1"
|
||||||
maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.1"
|
maplibre_annotation = "org.maplibre.gl:android-plugin-annotation-v9:2.0.1"
|
||||||
opusencoder = "io.element.android:opusencoder:1.1.0"
|
opusencoder = "io.element.android:opusencoder:1.1.0"
|
||||||
audiowaveform = "com.github.lincollincol:compose-audiowaveform:1.1.1"
|
|
||||||
|
|
||||||
# Analytics
|
# Analytics
|
||||||
posthog = "com.posthog.android:posthog:2.0.3"
|
posthog = "com.posthog.android:posthog:2.0.3"
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ dependencyResolutionManagement {
|
||||||
content {
|
content {
|
||||||
includeModule("com.github.UnifiedPush", "android-connector")
|
includeModule("com.github.UnifiedPush", "android-connector")
|
||||||
includeModule("com.github.matrix-org", "matrix-analytics-events")
|
includeModule("com.github.matrix-org", "matrix-analytics-events")
|
||||||
includeModule("com.github.lincollincol", "compose-audiowaveform")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// To have immediate access to Rust SDK versions
|
// To have immediate access to Rust SDK versions
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue