diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt index 2203902f..d8d550b4 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailFragment.kt @@ -8,7 +8,9 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import im.vector.matrix.android.api.Matrix +import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.riotredesign.R @@ -58,7 +60,9 @@ class RoomDetailFragment : RiotFragment() { val textMessage = composerEditText.text.toString() if (textMessage.isNotBlank()) { composerEditText.text = null - room.sendTextMessage(textMessage) + room.sendTextMessage(textMessage, object : MatrixCallback { + + }) } } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt index 514e139d..5f5810bb 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/timeline/TimelineEventController.kt @@ -58,7 +58,7 @@ class TimelineEventController(private val roomId: String, val item = when (event.root.type) { EventType.MESSAGE -> messageItemFactory.create(event, nextEvent, addDaySeparator, date) - else -> textItemFactory.create(event) + else -> textItemFactory.create(event) } item ?.onBind { timeline?.loadAround(index) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt index d9ebb1ec..04efa935 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/Event.kt @@ -6,7 +6,7 @@ import com.squareup.moshi.Types import im.vector.matrix.android.internal.di.MoshiProvider import java.lang.reflect.ParameterizedType -typealias Content = Map +typealias Content = Map @JsonClass(generateAdapter = true) data class Event( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/SendService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/SendService.kt index d4b085cb..6fbf87db 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/SendService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/SendService.kt @@ -1,10 +1,12 @@ package im.vector.matrix.android.api.session.room +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 interface SendService { - fun sendTextMessage(text: String): Cancelable + fun sendTextMessage(text: String, callback: MatrixCallback): Cancelable } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/EventFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/EventFactory.kt new file mode 100644 index 00000000..2dafe503 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/send/EventFactory.kt @@ -0,0 +1,44 @@ +package im.vector.matrix.android.api.session.room.send + +import im.vector.matrix.android.api.auth.data.Credentials +import im.vector.matrix.android.api.session.events.model.Content +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.room.model.MessageContent +import im.vector.matrix.android.api.session.room.model.MessageType +import im.vector.matrix.android.internal.di.MoshiProvider + +internal class EventFactory(private val credentials: Credentials) { + + private val moshi = MoshiProvider.providesMoshi() + + fun createTextEvent(roomId: String, text: String): Event { + val content = MessageContent(type = MessageType.MSGTYPE_TEXT, body = text) + + return Event( + roomId = roomId, + originServerTs = dummyOriginServerTs(), + sender = credentials.userId, + eventId = dummyEventId(roomId), + type = EventType.MESSAGE, + content = toContent(content) + ) + } + + private fun dummyOriginServerTs(): Long { + return System.currentTimeMillis() + } + + private fun dummyEventId(roomId: String): String { + return roomId + "-" + dummyOriginServerTs() + } + + @Suppress("UNCHECKED_CAST") + private inline fun toContent(data: T?): Content? { + val moshiAdapter = moshi.adapter(T::class.java) + val jsonValue = moshiAdapter.toJsonValue(data) + return jsonValue as? Content? + } + + +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt index 03b4b1e8..15ba170e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/ChunkEntityHelper.kt @@ -35,6 +35,10 @@ internal fun ChunkEntity.addAll(events: List, } } +internal fun ChunkEntity.updateDisplayIndexes() { + events.forEachIndexed { index, eventEntity -> eventEntity.displayIndex = index } +} + internal fun ChunkEntity.addOrUpdate(event: Event, direction: PaginationDirection, stateIndexOffset: Int = 0) { @@ -69,7 +73,7 @@ internal fun ChunkEntity.addOrUpdate(event: Event, internal fun ChunkEntity.lastStateIndex(direction: PaginationDirection, defaultValue: Int = 0): Int { return when (direction) { - PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex - PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex - } ?: defaultValue + PaginationDirection.FORWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING).findFirst()?.stateIndex + PaginationDirection.BACKWARDS -> events.where().sort(EventEntityFields.STATE_INDEX, Sort.ASCENDING).findFirst()?.stateIndex + } ?: defaultValue } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt index 0b69f2c0..07018c35 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/helper/RoomEntityHelper.kt @@ -13,7 +13,7 @@ internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) { } internal fun RoomEntity.addOrUpdate(chunkEntity: ChunkEntity) { - chunkEntity.events.forEachIndexed { index, eventEntity -> eventEntity.displayIndex = index } + chunkEntity.updateDisplayIndexes() if (!chunks.contains(chunkEntity)) { chunks.add(chunkEntity) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt index 8f2d1ed9..e4b2e93c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/SessionModule.kt @@ -23,6 +23,10 @@ internal class SessionModule(private val sessionParams: SessionParams) : Module override fun invoke(): ModuleDefinition = module(override = true) { + scope(DefaultSession.SCOPE) { + sessionParams + } + scope(DefaultSession.SCOPE) { RealmConfiguration.Builder() .name(sessionParams.credentials.userId) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt index 07b8a9fe..766fea9d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt @@ -6,6 +6,7 @@ import android.arch.paging.PagedList import com.zhuinden.monarchy.Monarchy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.model.EnrichedEvent +import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.SendService import im.vector.matrix.android.api.session.room.TimelineHolder @@ -61,14 +62,14 @@ internal data class DefaultRoom( private fun areAllMembersLoaded(): Boolean { return monarchy - .fetchAllCopiedSync { RoomEntity.where(it, roomId) } - .firstOrNull() - ?.areAllMembersLoaded ?: false + .fetchAllCopiedSync { RoomEntity.where(it, roomId) } + .firstOrNull() + ?.areAllMembersLoaded ?: false } - override fun sendTextMessage(text: String): Cancelable { - return sendService.sendTextMessage(text) + override fun sendTextMessage(text: String, callback: MatrixCallback): Cancelable { + return sendService.sendTextMessage(text, callback) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt index 31311bcd..28c9776f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomAPI.kt @@ -1,5 +1,6 @@ package im.vector.matrix.android.internal.session.room +import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.model.MessageContent import im.vector.matrix.android.internal.network.NetworkConstants @@ -62,7 +63,7 @@ internal interface RoomAPI { fun send(@Path("txId") txId: String, @Path("roomId") roomId: String, @Path("eventType") eventType: String, - @Body content: MessageContent + @Body content: Content? ): Call /** 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 14dee22b..598bd5f0 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 @@ -1,7 +1,9 @@ package im.vector.matrix.android.internal.session.room +import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.session.room.SendService import im.vector.matrix.android.api.session.room.TimelineHolder +import im.vector.matrix.android.api.session.room.send.EventFactory import im.vector.matrix.android.internal.session.DefaultSession import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersRequest import im.vector.matrix.android.internal.session.room.send.DefaultSendService @@ -39,9 +41,14 @@ class RoomModule : Module { DefaultTimelineHolder(roomId, get(), timelineBoundaryCallback) as TimelineHolder } + scope(DefaultSession.SCOPE) { + val sessionParams = get() + EventFactory(sessionParams.credentials) + } + factory { val roomId: String = it[0] - DefaultSendService(roomId) as SendService + DefaultSendService(roomId, get(), get()) as SendService } }.invoke() 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 015d5858..57e1fc6c 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 @@ -1,23 +1,43 @@ package im.vector.matrix.android.internal.session.room.send import androidx.work.* +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.room.SendService +import im.vector.matrix.android.api.session.room.send.EventFactory import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.database.helper.addOrUpdate +import im.vector.matrix.android.internal.database.helper.updateDisplayIndexes +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.timeline.PaginationDirection import im.vector.matrix.android.internal.util.CancelableWork import im.vector.matrix.android.internal.util.WorkerParamsFactory +import im.vector.matrix.android.internal.util.tryTransactionAsync import java.util.concurrent.TimeUnit private const val SEND_WORK = "SEND_WORK" -internal class DefaultSendService(private val roomId: String) : SendService { +internal class DefaultSendService(private val roomId: String, + private val eventFactory: EventFactory, + private val monarchy: Monarchy) : SendService { private val sendConstraints = Constraints.Builder() .setRequiredNetworkType(NetworkType.CONNECTED) .build() - override fun sendTextMessage(text: String): Cancelable { + override fun sendTextMessage(text: String, callback: MatrixCallback): Cancelable { + val event = eventFactory.createTextEvent(roomId, text) - val sendContentWorkerParams = SendEventWorker.Params(roomId, text) + monarchy.tryTransactionAsync { realm -> + val chunkEntity = ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) + ?: return@tryTransactionAsync + chunkEntity.addOrUpdate(event, PaginationDirection.FORWARDS) + chunkEntity.updateDisplayIndexes() + } + + val sendContentWorkerParams = SendEventWorker.Params(roomId, event) val workData = WorkerParamsFactory.toData(sendContentWorkerParams) val sendWork = OneTimeWorkRequestBuilder() @@ -34,4 +54,4 @@ internal class DefaultSendService(private val roomId: String) : SendService { } -} \ No newline at end of file +} 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 f490beb1..946b567c 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 @@ -4,12 +4,14 @@ import android.content.Context import androidx.work.Worker import androidx.work.WorkerParameters import com.squareup.moshi.JsonClass -import im.vector.matrix.android.api.session.events.model.EventType -import im.vector.matrix.android.api.session.room.model.MessageContent -import im.vector.matrix.android.api.session.room.model.MessageType +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.internal.database.model.EventEntity +import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.util.WorkerParamsFactory +import im.vector.matrix.android.internal.util.tryTransactionSync import org.koin.standalone.KoinComponent import org.koin.standalone.inject @@ -20,27 +22,37 @@ internal class SendEventWorker(context: Context, params: WorkerParameters) @JsonClass(generateAdapter = true) internal data class Params( val roomId: String, - val text: String + val event: Event ) private val roomAPI by inject() + private val monarchy by inject() override fun doWork(): Result { - val sendWorkerParameters = WorkerParamsFactory.fromData(inputData) + val params = WorkerParamsFactory.fromData(inputData) ?: return Result.FAILURE - val fakeId = sendWorkerParameters.roomId + "-" + System.currentTimeMillis() + if (params.event.eventId == null) { + return Result.FAILURE + } + val result = executeRequest { apiCall = roomAPI.send( - fakeId, - sendWorkerParameters.roomId, - EventType.MESSAGE, - MessageContent(MessageType.MSGTYPE_TEXT, sendWorkerParameters.text) + params.event.eventId, + params.roomId, + params.event.type, + params.event.content ) } + result.flatMap { sendResponse -> + monarchy.tryTransactionSync { realm -> + val dummyEventEntity = EventEntity.where(realm, params.event.eventId).findFirst() + dummyEventEntity?.eventId = sendResponse.eventId + } + } return result.fold({ Result.RETRY }, { Result.SUCCESS }) } -} \ No newline at end of file +}