Add KDoc to the libraries/mediaupload API interfaces

Document MediaSender and all its members, its two factories, MediaPreProcessor,
MaxUploadSizeProvider and MediaOptimizationConfigProvider.

The MediaSender doc explains the two-step shape of the API, which the individual
signatures do not convey: pre-processing and sending are separate so a preview
can be shown in between, and the temporary files that pre-processing creates are
only released by cleanUp, whether or not anything was ultimately sent.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benoit Marty
2026-08-14 09:44:42 +02:00
parent 414d2b5dd0
commit 23a346907c
4 changed files with 71 additions and 0 deletions
@@ -12,5 +12,6 @@ package io.element.android.libraries.mediaupload.api
* Provides the maximum upload size allowed by the Matrix server.
*/
fun interface MaxUploadSizeProvider {
/** Returns the maximum size in bytes a single upload may have, as advertised by the homeserver. */
suspend fun getMaxUploadSize(): Result<Long>
}
@@ -8,6 +8,10 @@
package io.element.android.libraries.mediaupload.api
/**
* Resolves how aggressively media should be compressed, combining the user's own preferences with the app defaults.
*/
fun interface MediaOptimizationConfigProvider {
/** Returns the configuration to pass to the pre-processing and sending calls. */
suspend fun get(): MediaOptimizationConfig
}
@@ -10,10 +10,18 @@ package io.element.android.libraries.mediaupload.api
import android.net.Uri
/**
* Prepares a media file for upload: resizing, transcoding and stripping metadata that would leak information about the sender.
*/
interface MediaPreProcessor {
/**
* Given a [uri] and [mimeType], pre-processes the media before it's uploaded, resizing, transcoding, and removing sensitive info from its metadata.
* If [deleteOriginal] is `true`, the file reference by the [uri] will be automatically deleted too when this process finishes.
*
* @param uri the media to process.
* @param mimeType the MIME type of that media.
* @param deleteOriginal whether to delete the source file once processing is done.
* @param mediaOptimizationConfig how aggressively the media should be compressed.
* @return a [Result] with the [MediaUploadInfo] containing all the info needed to begin the upload.
*/
suspend fun process(
@@ -13,31 +13,62 @@ import io.element.android.libraries.matrix.api.core.EventId
import io.element.android.libraries.matrix.api.room.JoinedRoom
import io.element.android.libraries.matrix.api.timeline.Timeline
/**
* Creates a [MediaSender] for the room of the current room scope; see [MediaSenderRoomFactory] when the room is not in scope.
*/
fun interface MediaSenderFactory {
/**
* Create a [MediaSender] for the given [Timeline.Mode], in the Room Scope.
*
* @param timelineMode the timeline the media should be sent to, which matters for threads in particular.
*/
fun create(
timelineMode: Timeline.Mode,
): MediaSender
}
/**
* Creates a [MediaSender] for an explicitly given room, used outside the room scope, for instance when sharing into a room.
*/
fun interface MediaSenderRoomFactory {
/**
* Create a [MediaSender] for the given [JoinedRoom], with timeline mode Live.
*
* @param room the room the media should be sent to.
*/
fun create(
room: JoinedRoom,
): MediaSender
}
/**
* Sends media to a room, taking care of the pre-processing the timeline API does not do: resizing, transcoding and stripping metadata.
*
* Sending is a two-step process, and the steps can be used separately so the UI can show a preview between them.
* The temporary files produced by pre-processing are only released by [cleanUp].
*/
interface MediaSender {
/**
* Resizes, transcodes and strips the metadata of a media file without sending anything yet.
*
* @param uri the media picked by the user.
* @param mimeType the MIME type of that media.
* @param mediaOptimizationConfig how aggressively the media should be compressed.
*/
suspend fun preProcessMedia(
uri: Uri,
mimeType: String,
mediaOptimizationConfig: MediaOptimizationConfig,
): Result<MediaUploadInfo>
/**
* Sends media that has already gone through [preProcessMedia].
*
* @param mediaUploadInfo the result of the pre-processing step.
* @param caption the text shown along with the media, or `null` for none.
* @param formattedCaption the caption as HTML, or `null` to send it unformatted.
* @param inReplyToEventId the event this message replies to, or `null` if it is not a reply.
*/
suspend fun sendPreProcessedMedia(
mediaUploadInfo: MediaUploadInfo,
caption: String?,
@@ -45,6 +76,16 @@ interface MediaSender {
inReplyToEventId: EventId?,
): Result<Unit>
/**
* Pre-processes and sends a media file in one go, suspending until the upload finishes.
*
* @param uri the media picked by the user.
* @param mimeType the MIME type of that media.
* @param caption the text shown along with the media, or `null` for none.
* @param formattedCaption the caption as HTML, or `null` to send it unformatted.
* @param inReplyToEventId the event this message replies to, or `null` if it is not a reply.
* @param mediaOptimizationConfig how aggressively the media should be compressed.
*/
suspend fun sendMedia(
uri: Uri,
mimeType: String,
@@ -54,6 +95,14 @@ interface MediaSender {
mediaOptimizationConfig: MediaOptimizationConfig,
): Result<Unit>
/**
* Sends a recorded voice message, which is never compressed and carries a waveform instead of a caption.
*
* @param uri the recorded audio file.
* @param mimeType the MIME type of that recording.
* @param waveForm the amplitudes used to draw the waveform.
* @param inReplyToEventId the event this message replies to, or `null` if it is not a reply.
*/
suspend fun sendVoiceMessage(
uri: Uri,
mimeType: String,
@@ -61,6 +110,14 @@ interface MediaSender {
inReplyToEventId: EventId? = null,
): Result<Unit>
/**
* Sends several already pre-processed media items as a single gallery message.
*
* @param mediaUploadInfos the pre-processed items, in the order they should appear.
* @param caption the text shown along with the gallery, or `null` for none.
* @param formattedCaption the caption as HTML, or `null` to send it unformatted.
* @param inReplyToEventId the event this message replies to, or `null` if it is not a reply.
*/
suspend fun sendGallery(
mediaUploadInfos: List<MediaUploadInfo>,
caption: String?,
@@ -68,5 +125,6 @@ interface MediaSender {
inReplyToEventId: EventId?,
): Result<Unit>
/** Deletes the temporary files produced by pre-processing; must be called even when nothing was ultimately sent. */
fun cleanUp()
}