From c47eeb9cec92118f003a30b476fd4947145b30be Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 4 Apr 2019 19:55:58 +0200 Subject: [PATCH] Send media: first working implementation. Then, need to fix local echo and handle other types than image. --- .../session/content/ContentAttachmentData.kt | 32 ++++++++ .../api/session/room/send/SendService.kt | 4 +- .../session/content/ContentUploadResponse.kt | 25 ++++++ .../session/content/ContentUploader.kt | 76 ++++++++++++++++++ .../session/content/UploadContentWorker.kt | 80 +++++++++++++++++++ .../session/group/GroupSummaryUpdater.kt | 1 - .../internal/session/room/RoomFactory.kt | 4 +- .../internal/session/room/RoomModule.kt | 8 +- .../session/room/media/MediaAttachment.kt | 2 - .../session/room/media/MediaUploader.kt | 23 ++++-- .../session/room/media/UploadMediaWorker.kt | 6 +- .../session/room/prune/EventsPruner.kt | 4 +- .../session/room/prune/PruneEventWorker.kt | 18 ++--- .../session/room/send/DefaultSendService.kt | 37 +++++---- ...entFactory.kt => LocalEchoEventFactory.kt} | 22 ++--- .../session/room/send/SendEventWorker.kt | 11 ++- .../home/room/detail/RoomDetailViewModel.kt | 5 +- 17 files changed, 293 insertions(+), 65 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploadResponse.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploader.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt rename matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/{EventFactory.kt => LocalEchoEventFactory.kt} (75%) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt new file mode 100644 index 00000000..e115ab8a --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/content/ContentAttachmentData.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.api.session.content + +import android.os.Parcelable +import kotlinx.android.parcel.Parcelize + +@Parcelize +data class ContentAttachmentData( + val size: Long = 0, + val duration: Long = 0, + val date: Long = 0, + val height: Long = 0, + val width: Long = 0, + val name: String? = null, + val path: String? = null, + val mimeType: String? = null +) : Parcelable \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt index 1821b7b4..6f6a18c8 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/SendService.kt @@ -19,7 +19,7 @@ package im.vector.matrix.android.api.session.room.send import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.session.room.media.MediaAttachment +import im.vector.matrix.android.api.session.content.ContentAttachmentData /** * This interface defines methods to send events in a room. It's implemented at the room level. @@ -34,6 +34,6 @@ interface SendService { */ fun sendTextMessage(text: String, callback: MatrixCallback): Cancelable - fun sendMedia(attachment: MediaAttachment, callback: MatrixCallback): Cancelable + fun sendMedia(attachment: ContentAttachmentData, callback: MatrixCallback): Cancelable } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploadResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploadResponse.kt new file mode 100644 index 00000000..e1f456fe --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploadResponse.kt @@ -0,0 +1,25 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.session.content + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass + +@JsonClass(generateAdapter = true) +data class ContentUploadResponse( + @Json(name = "content_uri") val contentUri: String +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploader.kt new file mode 100644 index 00000000..4814438c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/ContentUploader.kt @@ -0,0 +1,76 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.session.content + +import arrow.core.Try +import arrow.core.Try.Companion.raise +import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.matrix.android.internal.di.MoshiProvider +import okhttp3.HttpUrl +import okhttp3.MediaType +import okhttp3.OkHttpClient +import okhttp3.Request +import okhttp3.RequestBody +import java.io.File +import java.io.IOException + + +internal class ContentUploader(private val okHttpClient: OkHttpClient, + private val sessionParams: SessionParams) { + + private val moshi = MoshiProvider.providesMoshi() + private val responseAdapter = moshi.adapter(ContentUploadResponse::class.java) + + fun uploadFile(attachment: ContentAttachmentData): Try { + if (attachment.path == null || attachment.mimeType == null) { + return raise(RuntimeException()) + } + val file = File(attachment.path) + val urlString = sessionParams.homeServerConnectionConfig.homeServerUri.toString() + URI_PREFIX_CONTENT_API + "upload" + + val urlBuilder = HttpUrl.parse(urlString)?.newBuilder() + ?: return raise(RuntimeException()) + + val httpUrl = urlBuilder + .addQueryParameter( + "filename", attachment.name + ).build() + + val requestBody = RequestBody.create( + MediaType.parse(attachment.mimeType), + file + ) + val request = Request.Builder() + .url(httpUrl) + .post(requestBody) + .build() + + return Try { + okHttpClient.newCall(request).execute().use { response -> + if (!response.isSuccessful) { + throw IOException() + } else { + response.body()?.source()?.let { + responseAdapter.fromJson(it) + } + ?: throw IOException() + } + } + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt new file mode 100644 index 00000000..c3739864 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/content/UploadContentWorker.kt @@ -0,0 +1,80 @@ +/* + * Copyright 2019 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 im.vector.matrix.android.internal.session.content + +import android.content.Context +import androidx.work.Worker +import androidx.work.WorkerParameters +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.events.model.toContent +import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.room.model.message.MessageContent +import im.vector.matrix.android.api.session.room.model.message.MessageImageContent +import im.vector.matrix.android.internal.di.MatrixKoinComponent +import im.vector.matrix.android.internal.session.room.send.SendEventWorker +import im.vector.matrix.android.internal.util.WorkerParamsFactory +import org.koin.standalone.inject + +internal class UploadContentWorker(context: Context, params: WorkerParameters) + : Worker(context, params), MatrixKoinComponent { + + private val mediaUploader by inject() + + @JsonClass(generateAdapter = true) + internal data class Params( + val roomId: String, + val event: Event, + val attachment: ContentAttachmentData + ) + + override fun doWork(): Result { + val params = WorkerParamsFactory.fromData(inputData) + ?: return Result.failure() + + return mediaUploader + .uploadFile(params.attachment) + .fold({ handleFailure() }, { handleSuccess(params, it) }) + } + + private fun handleFailure(): Result { + return Result.retry() + } + + private fun handleSuccess(params: Params, contentUploadResponse: ContentUploadResponse): Result { + val event = updateEvent(params.event, contentUploadResponse.contentUri) + val sendParams = SendEventWorker.Params(params.roomId, event) + return Result.success(WorkerParamsFactory.toData(sendParams)) + } + + private fun updateEvent(event: Event, url: String): Event { + val messageContent: MessageContent = event.content.toModel() ?: return event + val updatedContent = when (messageContent) { + is MessageImageContent -> messageContent.update(url) + else -> messageContent + } + return event.copy(content = updatedContent.toContent()) + } + + private fun MessageImageContent.update(url: String): MessageImageContent { + return copy(url = url) + } + + +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt index d756b73c..826c53fc 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupSummaryUpdater.kt @@ -23,7 +23,6 @@ import androidx.work.OneTimeWorkRequestBuilder import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.internal.database.RealmLiveEntityObserver -import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.util.WorkerParamsFactory diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt index 46830ff5..0c8bc29b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt @@ -23,7 +23,7 @@ import im.vector.matrix.android.internal.session.room.members.RoomMemberExtracto import im.vector.matrix.android.internal.session.room.read.DefaultReadService import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask import im.vector.matrix.android.internal.session.room.send.DefaultSendService -import im.vector.matrix.android.internal.session.room.send.EventFactory +import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask import im.vector.matrix.android.internal.session.room.timeline.PaginationTask @@ -35,7 +35,7 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask, private val paginationTask: PaginationTask, private val contextOfEventTask: GetContextOfEventTask, private val setReadMarkersTask: SetReadMarkersTask, - private val eventFactory: EventFactory, + private val eventFactory: LocalEchoEventFactory, private val taskExecutor: TaskExecutor) { fun instantiate(roomId: String): Room { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt index 2bde72b8..b1f42772 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomModule.kt @@ -17,12 +17,12 @@ package im.vector.matrix.android.internal.session.room import im.vector.matrix.android.internal.session.DefaultSession -import im.vector.matrix.android.internal.session.room.media.MediaUploader +import im.vector.matrix.android.internal.session.content.ContentUploader import im.vector.matrix.android.internal.session.room.members.DefaultLoadRoomMembersTask import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.read.DefaultSetReadMarkersTask import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask -import im.vector.matrix.android.internal.session.room.send.EventFactory +import im.vector.matrix.android.internal.session.room.send.LocalEchoEventFactory import im.vector.matrix.android.internal.session.room.timeline.* import org.koin.dsl.module.module import retrofit2.Retrofit @@ -58,11 +58,11 @@ class RoomModule { } scope(DefaultSession.SCOPE) { - EventFactory(get()) + LocalEchoEventFactory(get()) } scope(DefaultSession.SCOPE) { - MediaUploader(get(), get()) + ContentUploader(get(), get()) } scope(DefaultSession.SCOPE) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaAttachment.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaAttachment.kt index 7205a370..1f77b451 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaAttachment.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaAttachment.kt @@ -18,7 +18,6 @@ package im.vector.matrix.android.internal.session.room.media -import android.net.Uri import android.os.Parcelable import kotlinx.android.parcel.Parcelize @@ -30,7 +29,6 @@ data class MediaAttachment( val height: Long = 0, val width: Long = 0, val name: String? = null, - val thumbnail: Uri? = null, val path: String? = null, val mimeType: String? = null ) : Parcelable \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaUploader.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaUploader.kt index f71d08ea..7fd9fe93 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaUploader.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/MediaUploader.kt @@ -20,11 +20,13 @@ package im.vector.matrix.android.internal.session.room.media import arrow.core.Try import im.vector.matrix.android.api.auth.data.SessionParams +import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.internal.session.content.URI_PREFIX_CONTENT_API +import okhttp3.HttpUrl import okhttp3.MediaType +import okhttp3.MultipartBody import okhttp3.OkHttpClient import okhttp3.Request -import okhttp3.RequestBody import java.io.File import java.io.IOException @@ -32,21 +34,28 @@ import java.io.IOException internal class MediaUploader(private val okHttpClient: OkHttpClient, private val sessionParams: SessionParams) { - fun uploadFile(attachment: MediaAttachment): Try { + fun uploadFile(attachment: ContentAttachmentData): Try { if (attachment.path == null || attachment.mimeType == null) { return Try.raise(RuntimeException()) } - val urlString = sessionParams.homeServerConnectionConfig.homeServerUri.toString() + URI_PREFIX_CONTENT_API + "upload" val file = File(attachment.path) + val urlString = sessionParams.homeServerConnectionConfig.homeServerUri.toString() + URI_PREFIX_CONTENT_API + "upload" - // create RequestBody instance from file - val requestFile = RequestBody.create( + val urlBuilder = HttpUrl.parse(urlString)?.newBuilder() + ?: return Try.raise(RuntimeException()) + + val httpUrl = urlBuilder + .addQueryParameter( + "filename", attachment.name + ).build() + + val requestBody = MultipartBody.create( MediaType.parse(attachment.mimeType), file ) val request = Request.Builder() - .url(urlString) - .post(requestFile) + .url(httpUrl) + .post(requestBody) .build() return okHttpClient.newCall(request).execute().use { response -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/UploadMediaWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/UploadMediaWorker.kt index 94912863..2e97ef5a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/UploadMediaWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/media/UploadMediaWorker.kt @@ -23,17 +23,19 @@ import androidx.work.Worker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import im.vector.matrix.android.internal.di.MatrixKoinComponent +import im.vector.matrix.android.api.session.content.ContentAttachmentData +import im.vector.matrix.android.internal.session.content.ContentUploader import im.vector.matrix.android.internal.util.WorkerParamsFactory import org.koin.standalone.inject internal class UploadMediaWorker(context: Context, params: WorkerParameters) : Worker(context, params), MatrixKoinComponent { - private val mediaUploader by inject() + private val mediaUploader by inject() @JsonClass(generateAdapter = true) internal data class Params( - val attachment: MediaAttachment + val attachment: ContentAttachmentData ) override fun doWork(): Result { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt index e4bf7770..ad17032a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/EventsPruner.kt @@ -35,7 +35,9 @@ internal class EventsPruner(monarchy: Monarchy) : override val query = Monarchy.Query { EventEntity.where(it, type = EventType.REDACTION) } override fun processChanges(inserted: List, updated: List, deleted: List) { - val redactionEvents = inserted.map { it.asDomain() } + val redactionEvents = inserted + .mapNotNull { it.asDomain().redacts } + val pruneEventWorkerParams = PruneEventWorker.Params(redactionEvents) val workData = WorkerParamsFactory.toData(pruneEventWorkerParams) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt index 786218a1..245aa551 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/prune/PruneEventWorker.kt @@ -21,7 +21,6 @@ import androidx.work.Worker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.internal.database.mapper.ContentMapper import im.vector.matrix.android.internal.database.model.EventEntity @@ -37,8 +36,8 @@ internal class PruneEventWorker(context: Context, ) : Worker(context, workerParameters), MatrixKoinComponent { @JsonClass(generateAdapter = true) - internal data class Params( - val redactionEvents: List + internal class Params( + val eventIdsToRedact: List ) private val monarchy by inject() @@ -48,18 +47,19 @@ internal class PruneEventWorker(context: Context, ?: return Result.failure() val result = monarchy.tryTransactionSync { realm -> - params.redactionEvents.forEach { event -> - pruneEvent(realm, event) + params.eventIdsToRedact.forEach { eventId -> + pruneEvent(realm, eventId) } } return result.fold({ Result.retry() }, { Result.success() }) } - private fun pruneEvent(realm: Realm, redactionEvent: Event?) { - if (redactionEvent == null || redactionEvent.redacts.isNullOrEmpty()) { + private fun pruneEvent(realm: Realm, eventIdToRedact: String) { + if (eventIdToRedact.isEmpty()) { return } - val eventToPrune = EventEntity.where(realm, eventId = redactionEvent.redacts).findFirst() + + val eventToPrune = EventEntity.where(realm, eventId = eventIdToRedact).findFirst() ?: return val allowedKeys = computeAllowedKeys(eventToPrune.type) @@ -87,7 +87,7 @@ internal class PruneEventWorker(context: Context, EventType.STATE_ROOM_ALIASES -> listOf("aliases") EventType.STATE_CANONICAL_ALIAS -> listOf("alias") EventType.FEEDBACK -> listOf("type", "target_event_id") - else -> emptyList() + else -> emptyList() } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt index 58a0c7e8..77793e49 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt @@ -16,17 +16,23 @@ package im.vector.matrix.android.internal.session.room.send -import androidx.work.* +import androidx.work.BackoffPolicy +import androidx.work.Constraints +import androidx.work.ExistingWorkPolicy +import androidx.work.NetworkType +import androidx.work.OneTimeWorkRequest +import androidx.work.OneTimeWorkRequestBuilder +import androidx.work.WorkManager import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.send.SendService import im.vector.matrix.android.api.util.Cancelable import im.vector.matrix.android.internal.database.helper.add import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom -import im.vector.matrix.android.internal.session.room.media.MediaAttachment -import im.vector.matrix.android.internal.session.room.media.UploadMediaWorker +import im.vector.matrix.android.internal.session.content.UploadContentWorker import im.vector.matrix.android.internal.session.room.timeline.PaginationDirection import im.vector.matrix.android.internal.util.CancelableWork import im.vector.matrix.android.internal.util.WorkerParamsFactory @@ -35,18 +41,19 @@ import java.util.concurrent.TimeUnit private const val SEND_WORK = "SEND_WORK" private const val BACKOFF_DELAY = 10_000L + private val WORK_CONSTRAINTS = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() internal class DefaultSendService(private val roomId: String, - private val eventFactory: EventFactory, + private val eventFactory: LocalEchoEventFactory, private val monarchy: Monarchy) : SendService { override fun sendTextMessage(text: String, callback: MatrixCallback): Cancelable { val event = eventFactory.createTextEvent(roomId, text) - saveLiveEvent(event) + saveLocalEcho(event) val sendWork = createSendEventWork(event) WorkManager.getInstance() .beginUniqueWork(SEND_WORK, ExistingWorkPolicy.APPEND, sendWork) @@ -55,12 +62,12 @@ internal class DefaultSendService(private val roomId: String, return CancelableWork(sendWork.id) } - override fun sendMedia(attachment: MediaAttachment, callback: MatrixCallback): Cancelable { + override fun sendMedia(attachment: ContentAttachmentData, callback: MatrixCallback): Cancelable { // Create an event with the media file path - val event = eventFactory.createImageEvent(roomId, attachment) - saveLiveEvent(event) - - val uploadWork = createUploadMediaWork(attachment) + val event = eventFactory.createMediaEvent(roomId, attachment).also { + saveLocalEcho(it) + } + val uploadWork = createUploadMediaWork(event, attachment) val sendWork = createSendEventWork(event) WorkManager.getInstance() @@ -70,10 +77,10 @@ internal class DefaultSendService(private val roomId: String, return CancelableWork(sendWork.id) } - private fun saveLiveEvent(event: Event) { + private fun saveLocalEcho(event: Event) { monarchy.tryTransactionAsync { realm -> val chunkEntity = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) - ?: return@tryTransactionAsync + ?: return@tryTransactionAsync chunkEntity.add(roomId, event, PaginationDirection.FORWARDS) } } @@ -89,11 +96,11 @@ internal class DefaultSendService(private val roomId: String, .build() } - private fun createUploadMediaWork(attachment: MediaAttachment): OneTimeWorkRequest { - val uploadMediaWorkerParams = UploadMediaWorker.Params(attachment) + private fun createUploadMediaWork(event: Event, attachment: ContentAttachmentData): OneTimeWorkRequest { + val uploadMediaWorkerParams = UploadContentWorker.Params(roomId, event, attachment) val uploadWorkData = WorkerParamsFactory.toData(uploadMediaWorkerParams) - return OneTimeWorkRequestBuilder() + return OneTimeWorkRequestBuilder() .setConstraints(WORK_CONSTRAINTS) .setInputData(uploadWorkData) .setBackoffCriteria(BackoffPolicy.LINEAR, BACKOFF_DELAY, TimeUnit.MILLISECONDS) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt similarity index 75% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EventFactory.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt index b0295f6c..4986ff9d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/EventFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/LocalEchoEventFactory.kt @@ -17,38 +17,38 @@ package im.vector.matrix.android.internal.session.room.send import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.toContent -import im.vector.matrix.android.api.session.events.model.toModel +import im.vector.matrix.android.api.session.room.model.message.ImageInfo import im.vector.matrix.android.api.session.room.model.message.MessageImageContent import im.vector.matrix.android.api.session.room.model.message.MessageTextContent import im.vector.matrix.android.api.session.room.model.message.MessageType -import im.vector.matrix.android.internal.session.room.media.MediaAttachment -internal class EventFactory(private val credentials: Credentials) { +internal class LocalEchoEventFactory(private val credentials: Credentials) { fun createTextEvent(roomId: String, text: String): Event { val content = MessageTextContent(type = MessageType.MSGTYPE_TEXT, body = text) return createEvent(roomId, content) } - fun createImageEvent(roomId: String, attachment: MediaAttachment): Event { + fun createMediaEvent(roomId: String, attachment: ContentAttachmentData): Event { val content = MessageImageContent( type = MessageType.MSGTYPE_IMAGE, body = attachment.name ?: "image", + info = ImageInfo( + mimeType = attachment.mimeType ?: "image/png", + width = attachment.width.toInt(), + height = attachment.height.toInt(), + size = attachment.size.toInt() + ), url = attachment.path ) return createEvent(roomId, content) } - fun updateImageEvent(event: Event, url: String): Event { - val imageContent = event.content.toModel() ?: return event - val updatedContent = imageContent.copy(url = url) - return event.copy(content = updatedContent.toContent()) - } - - fun createEvent(roomId: String, content: Any? = null): Event { + private fun createEvent(roomId: String, content: Any? = null): Event { return Event( roomId = roomId, originServerTs = dummyOriginServerTs(), diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt index bbe8455e..d956186b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendEventWorker.kt @@ -49,16 +49,17 @@ internal class SendEventWorker(context: Context, params: WorkerParameters) val params = WorkerParamsFactory.fromData(inputData) ?: return Result.failure() - if (params.event.eventId == null) { + val event = params.event + if (event.eventId == null) { return Result.failure() } val result = executeRequest { apiCall = roomAPI.send( - params.event.eventId, + event.eventId, params.roomId, - params.event.type, - params.event.content + event.type, + event.content ) } result.flatMap { sendResponse -> @@ -69,6 +70,4 @@ internal class SendEventWorker(context: Context, params: WorkerParameters) } return result.fold({ Result.retry() }, { Result.success() }) } - - } diff --git a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 58cb2c8a..9ad9efa7 100644 --- a/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -22,7 +22,7 @@ import com.jakewharton.rxrelay2.BehaviorRelay import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.Event -import im.vector.matrix.android.internal.session.room.media.MediaAttachment +import im.vector.matrix.android.api.session.content.ContentAttachmentData import im.vector.matrix.rx.rx import im.vector.riotredesign.core.platform.RiotViewModel import im.vector.riotredesign.features.home.room.VisibleRoomStore @@ -81,14 +81,13 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, private fun handleSendMedia(action: RoomDetailActions.SendMedia) { val attachment = action.mediaFiles.firstOrNull() ?.let { - MediaAttachment( + ContentAttachmentData( it.size, it.duration, it.date, it.height, it.width, it.name, - it.thumbnail, it.path, it.mimeType )