[MatrixSDK] finish mapping timeline and makes it compile
This commit is contained in:
parent
fb85f35525
commit
801eecfe8d
44 changed files with 370 additions and 242 deletions
|
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* 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.libraries.core.extensions
|
||||
|
||||
/**
|
||||
* Can be used to transform some Throwable into some other
|
||||
*/
|
||||
inline fun <R, T : R> Result<T>.mapFailure(transform: (exception: Throwable) -> Throwable): Result<R> {
|
||||
return when (val exception = exceptionOrNull()) {
|
||||
null -> this
|
||||
else -> Result.failure(transform(exception))
|
||||
}
|
||||
}
|
||||
|
|
@ -32,8 +32,6 @@ anvil {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
// api(projects.libraries.rustsdk)
|
||||
api(libs.matrix.sdk)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(libs.dagger)
|
||||
implementation(projects.libraries.core)
|
||||
|
|
|
|||
|
|
@ -21,8 +21,6 @@ import io.element.android.libraries.matrix.api.core.SessionId
|
|||
import io.element.android.libraries.matrix.api.media.MediaResolver
|
||||
import io.element.android.libraries.matrix.api.room.MatrixRoom
|
||||
import io.element.android.libraries.matrix.api.room.RoomSummaryDataSource
|
||||
import org.matrix.rustcomponents.sdk.MediaSource
|
||||
import java.io.Closeable
|
||||
|
||||
interface MatrixClient {
|
||||
val sessionId: SessionId
|
||||
|
|
@ -34,9 +32,9 @@ interface MatrixClient {
|
|||
suspend fun logout()
|
||||
suspend fun loadUserDisplayName(): Result<String>
|
||||
suspend fun loadUserAvatarURLString(): Result<String>
|
||||
suspend fun loadMediaContentForSource(source: MediaSource): Result<ByteArray>
|
||||
suspend fun loadMediaThumbnailForSource(
|
||||
source: MediaSource,
|
||||
suspend fun loadMediaContent(url: String): Result<ByteArray>
|
||||
suspend fun loadMediaThumbnail(
|
||||
url: String,
|
||||
width: Long,
|
||||
height: Long
|
||||
): Result<ByteArray>
|
||||
|
|
|
|||
|
|
@ -16,8 +16,6 @@
|
|||
|
||||
package io.element.android.libraries.matrix.api.auth
|
||||
|
||||
import org.matrix.rustcomponents.sdk.AuthenticationException
|
||||
|
||||
enum class AuthErrorCode(val value: String) {
|
||||
UNKNOWN("M_UNKNOWN"),
|
||||
USER_DEACTIVATED("M_USER_DEACTIVATED"),
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* 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.libraries.matrix.api.auth
|
||||
|
||||
sealed class AuthenticationException(message: String) : Exception(message) {
|
||||
class ClientMissing(message: String) : AuthenticationException(message)
|
||||
class InvalidServerName(message: String) : AuthenticationException(message)
|
||||
class SlidingSyncNotAvailable(message: String) : AuthenticationException(message)
|
||||
class SessionMissing(message: String) : AuthenticationException(message)
|
||||
class Generic(message: String) : AuthenticationException(message)
|
||||
}
|
||||
|
|
@ -24,8 +24,8 @@ import kotlinx.coroutines.flow.StateFlow
|
|||
interface MatrixAuthenticationService {
|
||||
fun isLoggedIn(): Flow<Boolean>
|
||||
suspend fun getLatestSessionId(): SessionId?
|
||||
suspend fun restoreSession(sessionId: SessionId): MatrixClient?
|
||||
suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient>
|
||||
fun getHomeserverDetails(): StateFlow<MatrixHomeServerDetails?>
|
||||
suspend fun setHomeserver(homeserver: String)
|
||||
suspend fun login(username: String, password: String): SessionId
|
||||
suspend fun setHomeserver(homeserver: String): Result<Unit>
|
||||
suspend fun login(username: String, password: String): Result<SessionId>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,17 +18,10 @@ package io.element.android.libraries.matrix.api.auth
|
|||
|
||||
import android.os.Parcelable
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import org.matrix.rustcomponents.sdk.HomeserverLoginDetails
|
||||
|
||||
@Parcelize
|
||||
data class MatrixHomeServerDetails(
|
||||
val url: String,
|
||||
val supportsPasswordLogin: Boolean,
|
||||
val authenticationIssuer: String?
|
||||
): Parcelable {
|
||||
constructor(homeserverLoginDetails: HomeserverLoginDetails) : this(
|
||||
homeserverLoginDetails.url(),
|
||||
homeserverLoginDetails.supportsPasswordLogin(),
|
||||
homeserverLoginDetails.authenticationIssuer()
|
||||
)
|
||||
}
|
||||
): Parcelable
|
||||
|
|
|
|||
|
|
@ -24,11 +24,11 @@ import io.element.android.libraries.matrix.api.media.VideoInfo
|
|||
|
||||
sealed interface TimelineEventContent
|
||||
|
||||
data class TimelineEventMessageContent(
|
||||
data class MessageContent(
|
||||
val body: String,
|
||||
val inReplyTo: UserId?,
|
||||
val isEdited: Boolean,
|
||||
val content: MessageContent?
|
||||
val type: MessageType?
|
||||
) : TimelineEventContent
|
||||
|
||||
object RedactedContent : TimelineEventContent
|
||||
|
|
@ -39,45 +39,45 @@ data class StickerContent(
|
|||
val url: String
|
||||
) : TimelineEventContent
|
||||
|
||||
sealed interface EncryptedMessage {
|
||||
data class OlmV1Curve25519AesSha2(
|
||||
val senderKey: String
|
||||
) : EncryptedMessage
|
||||
data class UnableToDecryptContent(
|
||||
val data: Data
|
||||
) : TimelineEventContent {
|
||||
sealed interface Data {
|
||||
data class OlmV1Curve25519AesSha2(
|
||||
val senderKey: String
|
||||
) : Data
|
||||
|
||||
data class MegolmV1AesSha2(
|
||||
val sessionId: String
|
||||
) : EncryptedMessage
|
||||
data class MegolmV1AesSha2(
|
||||
val sessionId: String
|
||||
) : Data
|
||||
|
||||
object Unknown : EncryptedMessage
|
||||
object Unknown : Data
|
||||
}
|
||||
}
|
||||
|
||||
data class UnableToDecryptContent(
|
||||
val message: EncryptedMessage
|
||||
) : TimelineEventContent
|
||||
|
||||
data class RoomMembership(
|
||||
data class RoomMembershipContent(
|
||||
val userId: UserId,
|
||||
val change: MembershipChange?
|
||||
) : TimelineEventContent
|
||||
|
||||
data class ProfileChange(
|
||||
data class ProfileChangeContent(
|
||||
val displayName: String?,
|
||||
val prevDisplayName: String?,
|
||||
val avatarUrl: String?,
|
||||
val prevAvatarUrl: String?
|
||||
) : TimelineEventContent
|
||||
|
||||
data class State(
|
||||
data class StateContent(
|
||||
val stateKey: String,
|
||||
val content: OtherState
|
||||
) : TimelineEventContent
|
||||
|
||||
data class FailedToParseMessageLike(
|
||||
data class FailedToParseMessageLikeContent(
|
||||
val eventType: String,
|
||||
val error: String
|
||||
) : TimelineEventContent
|
||||
|
||||
data class FailedToParseState(
|
||||
data class FailedToParseStateContent(
|
||||
val eventType: String,
|
||||
val stateKey: String,
|
||||
val error: String
|
||||
|
|
@ -85,9 +85,9 @@ data class FailedToParseState(
|
|||
|
||||
object UnknownContent : TimelineEventContent
|
||||
|
||||
sealed interface MessageContent
|
||||
sealed interface MessageType
|
||||
|
||||
object UnknownMessageContent : MessageContent
|
||||
object UnknownMessageType : MessageType
|
||||
|
||||
enum class MessageFormat {
|
||||
HTML, UNKNOWN
|
||||
|
|
@ -98,44 +98,44 @@ data class FormattedBody(
|
|||
val body: String
|
||||
)
|
||||
|
||||
data class EmoteMessageContent(
|
||||
data class EmoteMessageType(
|
||||
val body: String,
|
||||
val formatted: FormattedBody?
|
||||
) : MessageContent
|
||||
) : MessageType
|
||||
|
||||
data class ImageMessageContent(
|
||||
data class ImageMessageType(
|
||||
val body: String,
|
||||
val url: String,
|
||||
val info: ImageInfo?
|
||||
) : MessageContent
|
||||
) : MessageType
|
||||
|
||||
data class AudioMessageContent(
|
||||
data class AudioMessageType(
|
||||
var body: String,
|
||||
var url: String,
|
||||
var info: AudioInfo?
|
||||
) : MessageContent
|
||||
) : MessageType
|
||||
|
||||
data class VideoMessageContent(
|
||||
data class VideoMessageType(
|
||||
val body: String,
|
||||
val url: String,
|
||||
val info: VideoInfo?
|
||||
) : MessageContent
|
||||
) : MessageType
|
||||
|
||||
data class FileMessageContent(
|
||||
data class FileMessageType(
|
||||
val body: String,
|
||||
val url: String,
|
||||
val info: FileInfo?
|
||||
) : MessageContent
|
||||
) : MessageType
|
||||
|
||||
data class NoticeMessageContent(
|
||||
data class NoticeMessageType(
|
||||
val body: String,
|
||||
val formatted: FormattedBody?
|
||||
) : MessageContent
|
||||
) : MessageType
|
||||
|
||||
data class TextMessageContent(
|
||||
data class TextMessageType(
|
||||
val body: String,
|
||||
val formatted: FormattedBody?
|
||||
) : MessageContent
|
||||
) : MessageType
|
||||
|
||||
enum class MembershipChange {
|
||||
NONE,
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ anvil {
|
|||
|
||||
dependencies {
|
||||
// api(projects.libraries.rustsdk)
|
||||
api(libs.matrix.sdk)
|
||||
implementation(libs.matrix.sdk)
|
||||
implementation(projects.libraries.di)
|
||||
implementation(projects.libraries.matrix.api)
|
||||
implementation(libs.dagger)
|
||||
|
|
|
|||
|
|
@ -32,12 +32,12 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.withContext
|
||||
import org.matrix.rustcomponents.sdk.Client
|
||||
import org.matrix.rustcomponents.sdk.ClientDelegate
|
||||
import org.matrix.rustcomponents.sdk.MediaSource
|
||||
import org.matrix.rustcomponents.sdk.RequiredState
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncMode
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncRequestListFilters
|
||||
import org.matrix.rustcomponents.sdk.SlidingSyncViewBuilder
|
||||
import org.matrix.rustcomponents.sdk.TaskHandle
|
||||
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
|
|
@ -181,9 +181,9 @@ class RustMatrixClient constructor(
|
|||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "Fail to call logout on HS. Still delete local files.")
|
||||
}
|
||||
client.destroy()
|
||||
baseDirectory.deleteSessionDirectory(userID = client.userId())
|
||||
sessionStore.removeSession(client.userId())
|
||||
client.destroy()
|
||||
}
|
||||
|
||||
override suspend fun loadUserDisplayName(): Result<String> = withContext(dispatchers.io) {
|
||||
|
|
@ -199,23 +199,30 @@ class RustMatrixClient constructor(
|
|||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
override suspend fun loadMediaContentForSource(source: MediaSource): Result<ByteArray> =
|
||||
override suspend fun loadMediaContent(url: String): Result<ByteArray> =
|
||||
withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
client.getMediaContent(source).toUByteArray().toByteArray()
|
||||
mediaSourceFromUrl(url).use { source ->
|
||||
client.getMediaContent(source).toUByteArray().toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalUnsignedTypes::class)
|
||||
override suspend fun loadMediaThumbnailForSource(
|
||||
source: MediaSource,
|
||||
override suspend fun loadMediaThumbnail(
|
||||
url: String,
|
||||
width: Long,
|
||||
height: Long
|
||||
): Result<ByteArray> =
|
||||
withContext(dispatchers.io) {
|
||||
runCatching {
|
||||
client.getMediaThumbnail(source, width.toULong(), height.toULong()).toUByteArray()
|
||||
.toByteArray()
|
||||
mediaSourceFromUrl(url).use { source ->
|
||||
client.getMediaThumbnail(
|
||||
source = source,
|
||||
width = width.toULong(),
|
||||
height = height.toULong()
|
||||
).toUByteArray().toByteArray()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* 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.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.AuthenticationException
|
||||
import org.matrix.rustcomponents.sdk.AuthenticationException as RustAuthenticationException
|
||||
|
||||
fun Throwable.mapAuthenticationException(): Throwable {
|
||||
return when (this) {
|
||||
is RustAuthenticationException.ClientMissing -> AuthenticationException.ClientMissing(this.message!!)
|
||||
is RustAuthenticationException.Generic -> AuthenticationException.Generic(this.message!!)
|
||||
is RustAuthenticationException.InvalidServerName -> AuthenticationException.InvalidServerName(this.message!!)
|
||||
is RustAuthenticationException.SessionMissing -> AuthenticationException.SessionMissing(this.message!!)
|
||||
is RustAuthenticationException.SlidingSyncNotAvailable -> AuthenticationException.SlidingSyncNotAvailable(this.message!!)
|
||||
else -> this
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* 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.libraries.matrix.impl.auth
|
||||
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||
import org.matrix.rustcomponents.sdk.HomeserverLoginDetails
|
||||
|
||||
fun HomeserverLoginDetails.map(): MatrixHomeServerDetails = use {
|
||||
MatrixHomeServerDetails(
|
||||
url = url(),
|
||||
supportsPasswordLogin = supportsPasswordLogin(),
|
||||
authenticationIssuer = authenticationIssuer()
|
||||
)
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ package io.element.android.libraries.matrix.impl.auth
|
|||
|
||||
import com.squareup.anvil.annotations.ContributesBinding
|
||||
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
|
||||
import io.element.android.libraries.core.extensions.mapFailure
|
||||
import io.element.android.libraries.di.AppScope
|
||||
import io.element.android.libraries.di.SingleIn
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
|
|
@ -26,7 +27,6 @@ import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
|||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.impl.RustMatrixClient
|
||||
import io.element.android.libraries.matrix.impl.util.logError
|
||||
import io.element.android.libraries.sessionstorage.api.SessionData
|
||||
import io.element.android.libraries.sessionstorage.api.SessionStore
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
|
|
@ -39,7 +39,6 @@ import org.matrix.rustcomponents.sdk.Client
|
|||
import org.matrix.rustcomponents.sdk.ClientBuilder
|
||||
import org.matrix.rustcomponents.sdk.Session
|
||||
import org.matrix.rustcomponents.sdk.use
|
||||
import timber.log.Timber
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -63,10 +62,10 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
sessionStore.getLatestSession()?.userId?.let { UserId(it) }
|
||||
}
|
||||
|
||||
override suspend fun restoreSession(sessionId: SessionId) = withContext(coroutineDispatchers.io) {
|
||||
val sessionData = sessionStore.getSession(sessionId.value)
|
||||
if (sessionData != null) {
|
||||
try {
|
||||
override suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient> = withContext(coroutineDispatchers.io) {
|
||||
runCatching {
|
||||
val sessionData = sessionStore.getSession(sessionId.value)
|
||||
if (sessionData != null) {
|
||||
val client = ClientBuilder()
|
||||
.basePath(baseDirectory.absolutePath)
|
||||
.homeserverUrl(sessionData.homeserverUrl)
|
||||
|
|
@ -74,36 +73,39 @@ class RustMatrixAuthenticationService @Inject constructor(
|
|||
.use { it.build() }
|
||||
client.restoreSession(sessionData.toSession())
|
||||
createMatrixClient(client)
|
||||
} catch (throwable: Throwable) {
|
||||
logError(throwable)
|
||||
null
|
||||
} else {
|
||||
throw IllegalStateException("No session to restore with id $sessionId")
|
||||
}
|
||||
} else null
|
||||
}.mapFailure { failure ->
|
||||
failure.mapAuthenticationException()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getHomeserverDetails(): StateFlow<MatrixHomeServerDetails?> = currentHomeserver
|
||||
|
||||
override suspend fun setHomeserver(homeserver: String) {
|
||||
override suspend fun setHomeserver(homeserver: String): Result<Unit> =
|
||||
withContext(coroutineDispatchers.io) {
|
||||
authService.configureHomeserver(homeserver)
|
||||
val homeServerDetails = authService.homeserverDetails()?.use { MatrixHomeServerDetails(it) }
|
||||
if (homeServerDetails != null) {
|
||||
currentHomeserver.value = homeServerDetails.copy(url = homeserver)
|
||||
runCatching {
|
||||
authService.configureHomeserver(homeserver)
|
||||
val homeServerDetails = authService.homeserverDetails()?.map()
|
||||
if (homeServerDetails != null) {
|
||||
currentHomeserver.value = homeServerDetails.copy(url = homeserver)
|
||||
}
|
||||
}
|
||||
}.mapFailure { failure ->
|
||||
failure.mapAuthenticationException()
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String): SessionId =
|
||||
override suspend fun login(username: String, password: String): Result<SessionId> =
|
||||
withContext(coroutineDispatchers.io) {
|
||||
val client = try {
|
||||
authService.login(username, password, "ElementX Android", null)
|
||||
} catch (failure: Throwable) {
|
||||
Timber.e(failure, "Fail login")
|
||||
throw failure
|
||||
runCatching {
|
||||
val client = authService.login(username, password, "ElementX Android", null)
|
||||
val sessionData = client.use { it.session().toSessionData() }
|
||||
sessionStore.storeData(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
}
|
||||
val sessionData = client.use { it.session().toSessionData() }
|
||||
sessionStore.storeData(sessionData)
|
||||
SessionId(sessionData.userId)
|
||||
}.mapFailure { failure ->
|
||||
failure.mapAuthenticationException()
|
||||
}
|
||||
|
||||
private fun createMatrixClient(client: Client): MatrixClient {
|
||||
|
|
|
|||
|
|
@ -18,23 +18,15 @@ package io.element.android.libraries.matrix.impl.media
|
|||
|
||||
import io.element.android.libraries.matrix.api.MatrixClient
|
||||
import io.element.android.libraries.matrix.api.media.MediaResolver
|
||||
import org.matrix.rustcomponents.sdk.MediaSource
|
||||
import org.matrix.rustcomponents.sdk.mediaSourceFromUrl
|
||||
|
||||
internal class RustMediaResolver(private val client: MatrixClient) : MediaResolver {
|
||||
|
||||
override suspend fun resolve(url: String?, kind: MediaResolver.Kind): ByteArray? {
|
||||
if (url.isNullOrEmpty()) return null
|
||||
return mediaSourceFromUrl(url).use { mediaSource ->
|
||||
resolve(mediaSource, kind)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun resolve(mediaSource: MediaSource, kind: MediaResolver.Kind): ByteArray? {
|
||||
return when (kind) {
|
||||
is MediaResolver.Kind.Content -> client.loadMediaContentForSource(mediaSource)
|
||||
is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnailForSource(
|
||||
mediaSource,
|
||||
is MediaResolver.Kind.Content -> client.loadMediaContent(url)
|
||||
is MediaResolver.Kind.Thumbnail -> client.loadMediaThumbnail(
|
||||
url,
|
||||
kind.width.toLong(),
|
||||
kind.height.toLong()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,17 +17,17 @@
|
|||
package io.element.android.libraries.matrix.impl.timeline.item.event
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.AudioMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.EmoteMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FileMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FormattedBody
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ImageMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageFormat
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineEventMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.NoticeMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TextMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MessageContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownMessageType
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.VideoMessageType
|
||||
import io.element.android.libraries.matrix.impl.media.map
|
||||
import io.element.android.libraries.matrix.impl.media.useUrl
|
||||
import org.matrix.rustcomponents.sdk.Message
|
||||
|
|
@ -38,40 +38,40 @@ import org.matrix.rustcomponents.sdk.MessageFormat as RustMessageFormat
|
|||
|
||||
class EventMessageMapper {
|
||||
|
||||
fun map(message: Message): TimelineEventMessageContent = message.use {
|
||||
fun map(message: Message): MessageContent = message.use {
|
||||
val content = message.msgtype().use { type ->
|
||||
when (type) {
|
||||
is MessageType.Audio -> {
|
||||
AudioMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
AudioMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
}
|
||||
is MessageType.File -> {
|
||||
FileMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
FileMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
}
|
||||
is MessageType.Image -> {
|
||||
ImageMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
ImageMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
}
|
||||
is MessageType.Notice -> {
|
||||
NoticeMessageContent(type.content.body, type.content.formatted?.map())
|
||||
NoticeMessageType(type.content.body, type.content.formatted?.map())
|
||||
}
|
||||
is MessageType.Text -> {
|
||||
TextMessageContent(type.content.body, type.content.formatted?.map())
|
||||
TextMessageType(type.content.body, type.content.formatted?.map())
|
||||
}
|
||||
is MessageType.Emote -> {
|
||||
EmoteMessageContent(type.content.body, type.content.formatted?.map())
|
||||
EmoteMessageType(type.content.body, type.content.formatted?.map())
|
||||
}
|
||||
is MessageType.Video -> {
|
||||
VideoMessageContent(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
VideoMessageType(type.content.body, type.content.source.useUrl(), type.content.info?.map())
|
||||
}
|
||||
null -> {
|
||||
UnknownMessageContent
|
||||
UnknownMessageType
|
||||
}
|
||||
}
|
||||
}
|
||||
TimelineEventMessageContent(
|
||||
MessageContent(
|
||||
body = message.body(),
|
||||
inReplyTo = message.inReplyTo()?.let { UserId(it) },
|
||||
isEdited = message.isEdited(),
|
||||
content = content
|
||||
type = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -17,31 +17,33 @@
|
|||
package io.element.android.libraries.matrix.impl.timeline.item.event
|
||||
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLike
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseState
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseMessageLikeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.FailedToParseStateContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.MembershipChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChange
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.ProfileChangeContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RedactedContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembership
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.RoomMembershipContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.StickerContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.TimelineEventContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnableToDecryptContent
|
||||
import io.element.android.libraries.matrix.api.timeline.item.event.UnknownContent
|
||||
import io.element.android.libraries.matrix.impl.media.map
|
||||
import org.matrix.rustcomponents.sdk.TimelineItemContent
|
||||
import org.matrix.rustcomponents.sdk.TimelineItemContentKind
|
||||
import org.matrix.rustcomponents.sdk.EncryptedMessage as RustEncryptedMessage
|
||||
|
||||
class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMapper = EventMessageMapper()) {
|
||||
|
||||
fun map(content: TimelineItemContent): TimelineEventContent = content.use {
|
||||
when (val kind = content.kind()) {
|
||||
is TimelineItemContentKind.FailedToParseMessageLike -> {
|
||||
FailedToParseMessageLike(
|
||||
FailedToParseMessageLikeContent(
|
||||
eventType = kind.eventType,
|
||||
error = kind.error
|
||||
)
|
||||
}
|
||||
is TimelineItemContentKind.FailedToParseState -> {
|
||||
FailedToParseState(
|
||||
FailedToParseStateContent(
|
||||
eventType = kind.eventType,
|
||||
stateKey = kind.stateKey,
|
||||
error = kind.error
|
||||
|
|
@ -56,7 +58,7 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap
|
|||
}
|
||||
}
|
||||
is TimelineItemContentKind.ProfileChange -> {
|
||||
ProfileChange(
|
||||
ProfileChangeContent(
|
||||
displayName = kind.displayName,
|
||||
prevDisplayName = kind.prevDisplayName,
|
||||
avatarUrl = kind.avatarUrl,
|
||||
|
|
@ -67,7 +69,7 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap
|
|||
RedactedContent
|
||||
}
|
||||
is TimelineItemContentKind.RoomMembership -> {
|
||||
RoomMembership(
|
||||
RoomMembershipContent(
|
||||
UserId(kind.userId),
|
||||
MembershipChange.JOINED
|
||||
)
|
||||
|
|
@ -83,8 +85,18 @@ class TimelineEventContentMapper(private val eventMessageMapper: EventMessageMap
|
|||
)
|
||||
}
|
||||
is TimelineItemContentKind.UnableToDecrypt -> {
|
||||
UnknownContent
|
||||
UnableToDecryptContent(
|
||||
data = kind.msg.map()
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun RustEncryptedMessage.map(): UnableToDecryptContent.Data {
|
||||
return when (this) {
|
||||
is RustEncryptedMessage.MegolmV1AesSha2 -> UnableToDecryptContent.Data.MegolmV1AesSha2(sessionId)
|
||||
is RustEncryptedMessage.OlmV1Curve25519AesSha2 -> UnableToDecryptContent.Data.OlmV1Curve25519AesSha2(senderKey)
|
||||
RustEncryptedMessage.Unknown -> UnableToDecryptContent.Data.Unknown
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import io.element.android.libraries.matrix.test.media.FakeMediaResolver
|
|||
import io.element.android.libraries.matrix.test.room.FakeMatrixRoom
|
||||
import io.element.android.libraries.matrix.test.room.FakeRoomSummaryDataSource
|
||||
import kotlinx.coroutines.delay
|
||||
import org.matrix.rustcomponents.sdk.MediaSource
|
||||
|
||||
class FakeMatrixClient(
|
||||
override val sessionId: SessionId = A_SESSION_ID,
|
||||
|
|
@ -66,11 +65,11 @@ class FakeMatrixClient(
|
|||
return userAvatarURLString
|
||||
}
|
||||
|
||||
override suspend fun loadMediaContentForSource(source: MediaSource): Result<ByteArray> {
|
||||
override suspend fun loadMediaContent(url: String): Result<ByteArray> {
|
||||
return Result.success(ByteArray(0))
|
||||
}
|
||||
|
||||
override suspend fun loadMediaThumbnailForSource(source: MediaSource, width: Long, height: Long): Result<ByteArray> {
|
||||
override suspend fun loadMediaThumbnail(url: String, width: Long, height: Long): Result<ByteArray> {
|
||||
return Result.success(ByteArray(0))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,6 @@ import io.element.android.libraries.matrix.api.MatrixClient
|
|||
import io.element.android.libraries.matrix.api.auth.MatrixAuthenticationService
|
||||
import io.element.android.libraries.matrix.api.auth.MatrixHomeServerDetails
|
||||
import io.element.android.libraries.matrix.api.core.SessionId
|
||||
import io.element.android.libraries.matrix.test.A_HOMESERVER
|
||||
import io.element.android.libraries.matrix.test.A_USER_ID
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
|
@ -41,8 +40,8 @@ class FakeAuthenticationService : MatrixAuthenticationService {
|
|||
return null
|
||||
}
|
||||
|
||||
override suspend fun restoreSession(sessionId: SessionId): MatrixClient? {
|
||||
return null
|
||||
override suspend fun restoreSession(sessionId: SessionId): Result<MatrixClient> {
|
||||
return Result.failure(IllegalStateException())
|
||||
}
|
||||
|
||||
override fun getHomeserverDetails(): StateFlow<MatrixHomeServerDetails?> {
|
||||
|
|
@ -53,15 +52,16 @@ class FakeAuthenticationService : MatrixAuthenticationService {
|
|||
this.homeserver.value = homeserver
|
||||
}
|
||||
|
||||
override suspend fun setHomeserver(homeserver: String) {
|
||||
override suspend fun setHomeserver(homeserver: String): Result<Unit> {
|
||||
changeServerError?.let { throw it }
|
||||
delay(100)
|
||||
return Result.success(Unit)
|
||||
}
|
||||
|
||||
override suspend fun login(username: String, password: String): SessionId {
|
||||
override suspend fun login(username: String, password: String): Result<SessionId> {
|
||||
delay(100)
|
||||
loginError?.let { throw it }
|
||||
return A_USER_ID
|
||||
return Result.success(A_USER_ID)
|
||||
}
|
||||
|
||||
fun givenLoginError(throwable: Throwable?) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue