Add KDoc to matrix API room interfaces

Document BaseRoom, JoinedRoom, NotJoinedRoom, RoomAliasHelper and JoinRoom,
along with their members, and add the missing @param entries.

Fix the documentation of BaseRoom.getUpdatedIsEncrypted, which described the
room directory visibility instead of the encryption state it actually returns.

Behaviours taken from the implementations: BaseRoom.predecessorRoom swallows
errors into null, updateMembers only reads the cache on its first call,
JoinedRoom.syncUpdateFlow is an opaque change counter, notification settings
start in an Unknown state until updateRoomNotificationSettings is called, and
JoinRoom maps a Forbidden answer to Failures.UnauthorizedJoin.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benoit Marty
2026-08-13 19:02:12 +02:00
parent 5d6384e175
commit 0bd4dc39cd
5 changed files with 180 additions and 3 deletions
@@ -66,21 +66,30 @@ interface BaseRoom : Closeable {
*/
fun isDm() = roomInfoFlow.value.isDm
/**
* The room this one replaced when it was upgraded, or `null` if this room has no predecessor.
* Errors are swallowed and reported as `null`.
*/
fun predecessorRoom(): PredecessorRoom?
/**
* Try to load the room members and update the membersFlow.
* The first call may serve members from the local cache before the server response arrives; later calls always go to the server.
*/
suspend fun updateMembers()
/**
* Get the members of the room. Note: generally this should not be used, please use
* [membersStateFlow] and [updateMembers] instead.
*
* @param limit the maximum number of members to return.
*/
suspend fun getMembers(limit: Int = 5): Result<List<RoomMember>>
/**
* Will return an updated member or an error.
*
* @param userId the member to fetch.
*/
suspend fun getUpdatedMember(userId: UserId): Result<RoomMember>
@@ -103,6 +112,8 @@ interface BaseRoom : Closeable {
/**
* Gets the role of the user with the provided [userId] in the room.
*
* @param userId the member whose role is requested.
*/
suspend fun userRole(userId: UserId): Result<RoomMember.Role>
@@ -113,11 +124,15 @@ interface BaseRoom : Closeable {
/**
* Gets the display name of the user with the provided [userId] in the room.
*
* @param userId the member whose room specific display name is requested.
*/
suspend fun userDisplayName(userId: UserId): Result<String?>
/**
* Gets the avatar of the user with the provided [userId] in the room.
*
* @param userId the member whose room specific avatar is requested.
*/
suspend fun userAvatarUrl(userId: UserId): Result<String?>
@@ -138,6 +153,8 @@ interface BaseRoom : Closeable {
/**
* Sets the room as favorite or not, based on the [isFavorite] parameter.
*
* @param isFavorite true to mark the room as favorite, false to remove the flag.
*/
suspend fun setIsFavorite(isFavorite: Boolean): Result<Unit>
@@ -176,22 +193,29 @@ interface BaseRoom : Closeable {
suspend fun getRoomVisibility(): Result<RoomVisibility>
/**
* Returns the visibility for this room in the room directory, fetching it from the homeserver if needed.
* Returns whether this room is encrypted, based on the latest encryption state known to the SDK rather than on the cached [RoomInfo].
*/
suspend fun getUpdatedIsEncrypted(): Result<Boolean>
/**
* Store the given `ComposerDraft` in the state store of this room.
*
* @param composerDraft the unsent message content to remember.
* @param threadRoot the thread the draft belongs to, or `null` for the main timeline.
*/
suspend fun saveComposerDraft(composerDraft: ComposerDraft, threadRoot: ThreadId?): Result<Unit>
/**
* Retrieve the `ComposerDraft` stored in the state store for this room.
*
* @param threadRoot the thread whose draft is requested, or `null` for the main timeline.
*/
suspend fun loadComposerDraft(threadRoot: ThreadId?): Result<ComposerDraft?>
/**
* Clear the `ComposerDraft` stored in the state store for this room.
*
* @param threadRoot the thread whose draft should be cleared, or `null` for the main timeline.
*/
suspend fun clearComposerDraft(threadRoot: ThreadId?): Result<Unit>
@@ -202,10 +226,27 @@ interface BaseRoom : Closeable {
*/
suspend fun reportRoom(reason: String?): Result<Unit>
/**
* Declines the incoming call advertised by the given notification, letting the other devices of the room know.
*
* @param notificationEventId the id of the call notification event being declined.
*/
suspend fun declineCall(notificationEventId: EventId): Result<Unit>
/**
* Emits the id of each user who declines the call advertised by the given notification.
* Used to stop ringing once the callee has declined from another device.
*
* @param notificationEventId the id of the call notification event to watch.
*/
suspend fun subscribeToCallDecline(notificationEventId: EventId): Flow<UserId>
/**
* Returns the id of the thread the given event belongs to, fetching the event from the server if it is not cached locally.
*
* @param eventId the event whose thread is requested.
* @return the thread root id, or `null` if the event is not part of a thread.
*/
suspend fun threadRootIdForEvent(eventId: EventId): Result<ThreadId?>
/**
@@ -213,5 +254,6 @@ interface BaseRoom : Closeable {
*/
fun destroy()
/** Same as [destroy], so that a room can be used with `use { }`. */
override fun close() = destroy()
}
@@ -28,11 +28,28 @@ import io.element.android.libraries.matrix.api.widget.MatrixWidgetSettings
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.StateFlow
/**
* A room the current user has joined, adding to [BaseRoom] everything that requires membership: sending events, moderation and room settings.
*
* An instance owns SDK resources, in particular [liveTimeline], so it must be closed once no longer needed.
*/
interface JoinedRoom : BaseRoom {
/**
* A counter incremented every time a synced event is received on [liveTimeline], so callers can refresh data derived from the room.
* It carries no meaning beyond "something changed", and is only updated while it has subscribers.
*/
val syncUpdateFlow: StateFlow<Long>
/** The members currently typing in the room, never including the current user; starts as an empty list. */
val roomTypingMembersFlow: Flow<List<UserId>>
/** Emits when the verification state of other members' identities changes, for instance after a member resets their identity. */
val identityStateChangesFlow: Flow<List<IdentityStateChange>>
/**
* The notification settings that apply to this room.
* Starts as [RoomNotificationSettingsState.Unknown]: [updateRoomNotificationSettings] must be called to populate it.
*/
val roomNotificationSettingsStateFlow: StateFlow<RoomNotificationSettingsState>
/**
@@ -45,6 +62,7 @@ interface JoinedRoom : BaseRoom {
*/
val liveTimeline: Timeline
/** Gives access to the paginated list of threads started in this room. */
val threadsListService: ThreadsListService
/**
@@ -55,6 +73,14 @@ interface JoinedRoom : BaseRoom {
createTimelineParams: CreateTimelineParams,
): Result<Timeline>
/**
* Replaces the content of an event previously sent by the current user, by sending an edit of it.
*
* @param eventId the event to edit.
* @param body the new content, as plain text.
* @param htmlBody the new content as HTML, or `null` when the message has no formatted version.
* @param intentionalMentions the users and rooms this new content deliberately mentions.
*/
suspend fun editMessage(eventId: EventId, body: String, htmlBody: String?, intentionalMentions: List<IntentionalMention>): Result<Unit>
/**
@@ -63,12 +89,28 @@ interface JoinedRoom : BaseRoom {
*/
suspend fun typingNotice(isTyping: Boolean): Result<Unit>
/**
* Invites a user to this room.
*
* @param id the user to invite.
*/
suspend fun inviteUserById(id: UserId): Result<Unit>
/**
* Uploads [data] as the new avatar of the room, not of the current user.
*
* @param mimeType the MIME type of the image, for instance `image/jpeg`.
* @param data the raw bytes of the image.
*/
suspend fun updateAvatar(mimeType: String, data: ByteArray): Result<Unit>
/** Removes the avatar of the room. */
suspend fun removeAvatar(): Result<Unit>
/**
* Refreshes [roomNotificationSettingsStateFlow], which moves to a pending state and then to a ready or error state.
* The failure is reported through that flow as well as through the returned [Result].
*/
suspend fun updateRoomNotificationSettings(): Result<Unit>
/**
@@ -83,11 +125,15 @@ interface JoinedRoom : BaseRoom {
/**
* Update the room's visibility in the room directory.
*
* @param roomVisibility whether the room should be listed publicly in the directory.
*/
suspend fun updateRoomVisibility(roomVisibility: RoomVisibility): Result<Unit>
/**
* Update room history visibility for this room.
* Update room history visibility for this room, i.e. how much of the past a new member is allowed to read.
*
* @param historyVisibility the new history visibility to apply.
*/
suspend fun updateHistoryVisibility(historyVisibility: RoomHistoryVisibility): Result<Unit>
@@ -98,6 +144,8 @@ interface JoinedRoom : BaseRoom {
* - `true` if the room alias didn't exist and it's now published.
* - `false` if the room alias was already present so it couldn't be
* published.
*
* @param roomAlias the alias to publish.
*/
suspend fun publishRoomAliasInRoomDirectory(roomAlias: RoomAlias): Result<Boolean>
@@ -108,6 +156,8 @@ interface JoinedRoom : BaseRoom {
* - `true` if the room alias was present and it's now removed from the
* room directory.
* - `false` if the room alias didn't exist so it couldn't be removed.
*
* @param roomAlias the alias to remove.
*/
suspend fun removeRoomAliasFromRoomDirectory(roomAlias: RoomAlias): Result<Boolean>
@@ -117,26 +167,74 @@ interface JoinedRoom : BaseRoom {
suspend fun enableEncryption(): Result<Unit>
/**
* Update the join rule for this room.
* Update the join rule for this room, i.e. who is allowed to join it.
*
* @param joinRule the new join rule to apply.
*/
suspend fun updateJoinRule(joinRule: JoinRule): Result<Unit>
/**
* Changes the power level of individual members, which is how roles such as moderator or administrator are granted.
*
* @param changes the new power level to apply to each user.
*/
suspend fun updateUsersRoles(changes: List<UserRoleChange>): Result<Unit>
/**
* Changes the power level required for each action in the room, such as inviting, kicking or changing the room name.
*
* @param roomPowerLevelsValues the new threshold for every action.
*/
suspend fun updatePowerLevels(roomPowerLevelsValues: RoomPowerLevelsValues): Result<Unit>
/** Restores the power levels required for each action to the defaults the room was created with. */
suspend fun resetPowerLevels(): Result<Unit>
/**
* Changes the name of the room.
*
* @param name the new room name.
*/
suspend fun setName(name: String): Result<Unit>
/**
* Changes the topic of the room.
*
* @param topic the new room topic.
*/
suspend fun setTopic(topic: String): Result<Unit>
/**
* Reports an event to the homeserver moderators, optionally ignoring its sender at the same time.
*
* @param eventId the event being reported.
* @param reason the explanation sent to the moderators.
* @param blockUserId the sender to also add to the ignore list, or `null` to only report the event.
*/
suspend fun reportContent(eventId: EventId, reason: String, blockUserId: UserId?): Result<Unit>
/**
* Removes a member from the room; they are able to join again unless they are also banned.
*
* @param userId the member to remove.
* @param reason the reason recorded in the membership event, or `null` for none.
*/
suspend fun kickUser(userId: UserId, reason: String? = null): Result<Unit>
/**
* Bans a user from the room, removing them if they are a member and preventing them from joining again.
*
* @param userId the user to ban.
* @param reason the reason recorded in the membership event, or `null` for none.
*/
suspend fun banUser(userId: UserId, reason: String? = null): Result<Unit>
/**
* Lifts the ban on a user, allowing them to join the room again; it does not invite them back.
*
* @param userId the user to unban.
* @param reason the reason recorded in the membership event, or `null` for none.
*/
suspend fun unbanUser(userId: UserId, reason: String? = null): Result<Unit>
/**
@@ -161,6 +259,12 @@ interface JoinedRoom : BaseRoom {
*/
fun getWidgetDriver(widgetSettings: MatrixWidgetSettings): Result<MatrixWidgetDriver>
/**
* Enables or disables the send queue of this room only; see [io.element.android.libraries.matrix.api.MatrixClient.setAllSendQueuesEnabled]
* for the session-wide equivalent and for why a queue disables itself.
*
* @param enabled whether the send queue of this room should be enabled.
*/
suspend fun setSendQueueEnabled(enabled: Boolean)
/**
@@ -12,7 +12,13 @@ import io.element.android.libraries.matrix.api.room.preview.RoomPreviewInfo
/** A reference to a room either invited, knocked or banned. */
interface NotJoinedRoom : AutoCloseable {
/** What is publicly known about the room: name, topic, avatar, join rule and member count. */
val previewInfo: RoomPreviewInfo
/**
* The local room, available when the user has a membership in it, for instance after being invited.
* It is `null` for a room only discovered through a preview, and closing this instance also closes it.
*/
val localRoom: BaseRoom?
/**
@@ -10,7 +10,21 @@ package io.element.android.libraries.matrix.api.room.alias
import io.element.android.libraries.matrix.api.core.RoomAlias
/**
* Helpers to build and validate room aliases, delegating to the SDK so that the app and the server agree on what is acceptable.
*/
interface RoomAliasHelper {
/**
* Turns a room display name into a usable alias local part, dropping the characters that are not allowed in an alias.
*
* @param name the room display name to derive the alias from.
*/
fun roomAliasNameFromRoomDisplayName(name: String): String
/**
* Whether the given alias is well-formed; this is a format check only and says nothing about the alias being taken.
*
* @param roomAlias the alias to validate.
*/
fun isRoomAliasValid(roomAlias: RoomAlias): Boolean
}
@@ -11,7 +11,18 @@ package io.element.android.libraries.matrix.api.room.join
import im.vector.app.features.analytics.plan.JoinedRoom
import io.element.android.libraries.matrix.api.core.RoomIdOrAlias
/**
* Joins a room and reports the join to analytics, so that callers do not have to do both.
*/
interface JoinRoom {
/**
* Joins the room, picking the right SDK call depending on whether an id or an alias was given, and captures an analytics event on success.
* A `Forbidden` answer from the server is translated into [Failures.UnauthorizedJoin] so callers can tell it apart from other errors.
*
* @param roomIdOrAlias the id or the alias of the room to join.
* @param serverNames servers to ask about the room, ignored when joining by alias.
* @param trigger what made the user join, recorded in the analytics event.
*/
suspend operator fun invoke(
roomIdOrAlias: RoomIdOrAlias,
serverNames: List<String>,