Merge branch 'develop' of https://github.com/vector-im/element-x-android into langleyd/live_waveform

This commit is contained in:
David Langley
2023-10-27 13:54:18 +01:00
37 changed files with 153 additions and 91 deletions
@@ -38,6 +38,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.FloatingActionButtonDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
@@ -59,6 +60,8 @@ import io.element.android.features.messages.impl.timeline.components.TimelineIte
import io.element.android.features.messages.impl.timeline.components.TimelineItemVirtualRow
import io.element.android.features.messages.impl.timeline.components.group.GroupHeaderView
import io.element.android.features.messages.impl.timeline.components.virtual.TimelineLoadingMoreIndicator
import io.element.android.features.messages.impl.timeline.di.LocalTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.di.aFakeTimelineItemPresenterFactories
import io.element.android.features.messages.impl.timeline.model.TimelineItem
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContent
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemEventContentProvider
@@ -333,15 +336,19 @@ internal fun TimelineViewPreview(
@PreviewParameter(TimelineItemEventContentProvider::class) content: TimelineItemEventContent
) = ElementPreview {
val timelineItems = aTimelineItemList(content)
TimelineView(
state = aTimelineState(timelineItems),
onMessageClicked = {},
onTimestampClicked = {},
onUserDataClicked = {},
onMessageLongClicked = {},
onReactionClicked = { _, _ -> },
onReactionLongClicked = { _, _ -> },
onMoreReactionsClicked = {},
onSwipeToReply = {},
)
CompositionLocalProvider(
LocalTimelineItemPresenterFactories provides aFakeTimelineItemPresenterFactories(),
) {
TimelineView(
state = aTimelineState(timelineItems),
onMessageClicked = {},
onTimestampClicked = {},
onUserDataClicked = {},
onMessageLongClicked = {},
onReactionClicked = { _, _ -> },
onReactionLongClicked = { _, _ -> },
onMoreReactionsClicked = {},
onSwipeToReply = {},
)
}
}
@@ -0,0 +1,34 @@
/*
* 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.timeline.di
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.voicemessages.timeline.VoiceMessageState
import io.element.android.features.messages.impl.voicemessages.timeline.aVoiceMessageState
import io.element.android.libraries.architecture.Presenter
/**
* A fake [TimelineItemPresenterFactories] for screenshot tests.
*/
fun aFakeTimelineItemPresenterFactories() = TimelineItemPresenterFactories(
mapOf(
Pair(
TimelineItemVoiceContent::class.java,
TimelineItemPresenterFactory<TimelineItemVoiceContent, VoiceMessageState> { Presenter { aVoiceMessageState() } },
),
)
)
@@ -29,7 +29,6 @@ import io.element.android.features.messages.impl.timeline.model.event.TimelineIt
import io.element.android.features.messages.impl.timeline.model.event.TimelineItemVoiceContent
import io.element.android.features.messages.impl.timeline.util.FileExtensionExtractor
import io.element.android.features.messages.impl.timeline.util.toHtmlDocument
import io.element.android.features.messages.impl.voicemessages.fromMSC3246range
import io.element.android.libraries.androidutils.filesize.FileSizeFormatter
import io.element.android.libraries.core.mimetype.MimeTypes
import io.element.android.libraries.featureflag.api.FeatureFlagService
@@ -118,7 +117,7 @@ class TimelineItemContentMessageFactory @Inject constructor(
mediaSource = messageType.source,
duration = messageType.info?.duration ?: Duration.ZERO,
mimeType = messageType.info?.mimetype ?: MimeTypes.OctetStream,
waveform = messageType.details?.waveform?.fromMSC3246range()?.toImmutableList() ?: persistentListOf(),
waveform = messageType.details?.waveform?.toImmutableList() ?: persistentListOf(),
)
else -> TimelineItemAudioContent(
body = messageType.body,
@@ -28,8 +28,12 @@ class TimelineItemEventContentProvider : PreviewParameterProvider<TimelineItemEv
aTimelineItemVideoContent(),
aTimelineItemFileContent(),
aTimelineItemFileContent("A bigger file name which doesn't fit.pdf"),
aTimelineItemAudioContent(),
aTimelineItemAudioContent("An even bigger bigger bigger bigger bigger bigger bigger sound name which doesn't fit .mp3"),
aTimelineItemVoiceContent(),
aTimelineItemLocationContent(),
aTimelineItemLocationContent("Location description"),
aTimelineItemPollContent(),
aTimelineItemNoticeContent(),
aTimelineItemRedactedContent(),
aTimelineItemTextContent(),
@@ -1,28 +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
/**
* Resizes the given [0;1024] int list as per unstable MSC3246 spec
* to a [0;1] range float list to be used for waveform rendering.
*/
internal fun List<Int>.fromMSC3246range(): List<Float> = map { it / 1024f }
/**
* Resizes the given [0;1] float list to [0;1024] int list as per unstable MSC3246 spec.
*/
internal fun List<Float>.toMSC3246range(): List<Int> = map { (it * 1024).toInt() }
@@ -28,7 +28,6 @@ import androidx.compose.runtime.setValue
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import io.element.android.features.messages.impl.voicemessages.VoiceMessageException
import io.element.android.features.messages.impl.voicemessages.toMSC3246range
import io.element.android.libraries.architecture.Presenter
import io.element.android.libraries.di.RoomScope
import io.element.android.libraries.di.SingleIn
@@ -220,7 +219,7 @@ class VoiceMessageComposerPresenter @Inject constructor(
val result = mediaSender.sendVoiceMessage(
uri = file.toUri(),
mimeType = mimeType,
waveForm = waveform.toMSC3246range(),
waveForm = waveform,
)
if (result.isFailure) {
@@ -21,35 +21,41 @@ import androidx.compose.ui.tooling.preview.PreviewParameterProvider
open class VoiceMessageStateProvider : PreviewParameterProvider<VoiceMessageState> {
override val values: Sequence<VoiceMessageState>
get() = sequenceOf(
VoiceMessageState(
aVoiceMessageState(
VoiceMessageState.Button.Downloading,
progress = 0f,
time = "0:00",
eventSink = {},
),
VoiceMessageState(
aVoiceMessageState(
VoiceMessageState.Button.Retry,
progress = 0.5f,
time = "0:01",
eventSink = {}
),
VoiceMessageState(
aVoiceMessageState(
VoiceMessageState.Button.Play,
progress = 1f,
time = "1:00",
eventSink = {}
),
VoiceMessageState(
aVoiceMessageState(
VoiceMessageState.Button.Pause,
progress = 0.2f,
time = "10:00",
eventSink = {}
),
VoiceMessageState(
aVoiceMessageState(
VoiceMessageState.Button.Disabled,
progress = 0.2f,
time = "30:00",
eventSink = {}
),
)
}
fun aVoiceMessageState(
button: VoiceMessageState.Button = VoiceMessageState.Button.Play,
progress: Float = 0f,
time: String = "1:00",
) = VoiceMessageState(
button = button,
progress = progress,
time = time,
eventSink = {},
)