diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser
index de59e1fa..e57bc19d 100644
Binary files a/.idea/caches/build_file_checksums.ser and b/.idea/caches/build_file_checksums.ser differ
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 bb4d617f..b9211003 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
@@ -50,6 +50,13 @@ class RoomDetailFragment : RiotFragment() {
room.loadRoomMembersIfNeeded()
room.liveTimeline().observe(this, Observer { renderEvents(it) })
room.roomSummary.observe(this, Observer { renderRoomSummary(it) })
+ sendButton.setOnClickListener {
+ val textMessage = composerEditText.text.toString()
+ if (textMessage.isNotBlank()) {
+ composerEditText.text = null
+ room.sendTextMessage(textMessage)
+ }
+ }
}
private fun setupToolbar() {
diff --git a/app/src/main/res/drawable-hdpi/ic_send_white.png b/app/src/main/res/drawable-hdpi/ic_send_white.png
new file mode 100644
index 00000000..f133cdbe
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_send_white.png differ
diff --git a/app/src/main/res/drawable-mdpi/ic_send_white.png b/app/src/main/res/drawable-mdpi/ic_send_white.png
new file mode 100644
index 00000000..34e49af7
Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_send_white.png differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_send_white.png b/app/src/main/res/drawable-xhdpi/ic_send_white.png
new file mode 100644
index 00000000..e5f9ba41
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_send_white.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_send_white.png b/app/src/main/res/drawable-xxhdpi/ic_send_white.png
new file mode 100644
index 00000000..0ba718b6
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_send_white.png differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_send_white.png b/app/src/main/res/drawable-xxxhdpi/ic_send_white.png
new file mode 100644
index 00000000..f02b6453
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_send_white.png differ
diff --git a/app/src/main/res/layout/fragment_room_detail.xml b/app/src/main/res/layout/fragment_room_detail.xml
index 0f22a314..b34ff8ed 100644
--- a/app/src/main/res/layout/fragment_room_detail.xml
+++ b/app/src/main/res/layout/fragment_room_detail.xml
@@ -73,9 +73,53 @@
android:id="@+id/recyclerView"
android:layout_width="0dp"
android:layout_height="0dp"
- app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintBottom_toTopOf="@+id/composerDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/toolbar" />
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle
index 5b5d5c7a..8438f451 100644
--- a/matrix-sdk-android/build.gradle
+++ b/matrix-sdk-android/build.gradle
@@ -75,6 +75,9 @@ dependencies {
// Paging
implementation "android.arch.paging:runtime:1.0.1"
+ // Work
+ implementation "android.arch.work:work-runtime-ktx:1.0.0-alpha10"
+
// FP
implementation "io.arrow-kt:arrow-core:$arrow_version"
implementation "io.arrow-kt:arrow-instances-core:$arrow_version"
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 2763506b..62a24bc8 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,12 +6,14 @@ import com.squareup.moshi.JsonClass
import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.legacy.util.JsonUtils
+typealias Content = Map
+
@JsonClass(generateAdapter = true)
data class Event(
@Json(name = "type") val type: String,
@Json(name = "event_id") val eventId: String?,
- @Json(name = "content") val content: Map? = null,
- @Json(name = "prev_content") val prevContent: Map? = null,
+ @Json(name = "content") val content: Content? = null,
+ @Json(name = "prev_content") val prevContent: Content? = null,
@Json(name = "origin_server_ts") val originServerTs: Long? = null,
@Json(name = "sender") val sender: String? = null,
@Json(name = "state_key") val stateKey: String? = null,
@@ -39,7 +41,7 @@ data class Event(
return toModel(prevContent)
}
- inline fun toModel(data: Map?): T? {
+ inline fun toModel(data: Content?): T? {
val moshi = MoshiProvider.providesMoshi()
val moshiAdapter = moshi.adapter(T::class.java)
return moshiAdapter.fromJsonValue(data)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt
index ac58a11b..412131f1 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt
@@ -5,7 +5,7 @@ import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.util.Cancelable
-interface Room : TimelineHolder {
+interface Room : TimelineHolder, SendService {
val roomId: String
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
new file mode 100644
index 00000000..d4b085cb
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/SendService.kt
@@ -0,0 +1,10 @@
+package im.vector.matrix.android.api.session.room
+
+import im.vector.matrix.android.api.util.Cancelable
+
+interface SendService {
+
+ fun sendTextMessage(text: String): Cancelable
+
+
+}
\ No newline at end of file
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 c8bb8881..1388c141 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
@@ -7,6 +7,7 @@ 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.room.Room
+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.model.Membership
import im.vector.matrix.android.api.session.room.model.MyMembership
@@ -28,11 +29,11 @@ internal data class DefaultRoom(
override val myMembership: MyMembership
) : Room, KoinComponent {
-
private val loadRoomMembersRequest by inject()
private val syncTokenStore by inject()
private val monarchy by inject()
private val timelineHolder by inject(parameters = { parametersOf(roomId) })
+ private val sendService by inject(parameters = { parametersOf(roomId) })
override val roomSummary: LiveData by lazy {
val liveData = monarchy
@@ -66,4 +67,8 @@ internal data class DefaultRoom(
}
+ override fun sendTextMessage(text: String): Cancelable {
+ return sendService.sendTextMessage(text)
+ }
+
}
\ 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 9b3d677e..655cd33d 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,12 +1,14 @@
package im.vector.matrix.android.internal.session.room
+import im.vector.matrix.android.api.session.room.model.MessageContent
import im.vector.matrix.android.internal.network.NetworkConstants
import im.vector.matrix.android.internal.session.room.members.RoomMembersResponse
+import im.vector.matrix.android.internal.session.room.send.SendResponse
import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEvent
-import kotlinx.coroutines.Deferred
import retrofit2.Call
-import retrofit2.Response
+import retrofit2.http.Body
import retrofit2.http.GET
+import retrofit2.http.PUT
import retrofit2.http.Path
import retrofit2.http.Query
@@ -46,4 +48,20 @@ internal interface RoomAPI {
): Call
+ /**
+ * Send an event to a room.
+ *
+ * @param txId the transaction Id
+ * @param roomId the room id
+ * @param eventType the event type
+ * @param content the event content
+ */
+ @PUT(NetworkConstants.URI_API_PREFIX_PATH_R0 + "rooms/{roomId}/send/{eventType}/{txId}")
+ fun send(@Path("txId") txId: String,
+ @Path("roomId") roomId: String,
+ @Path("eventType") eventType: String,
+ @Body content: MessageContent
+ ): Call
+
+
}
\ No newline at end of file
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 6a5538b5..bfd9052e 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,12 +1,13 @@
package im.vector.matrix.android.internal.session.room
+import im.vector.matrix.android.api.session.room.SendService
import im.vector.matrix.android.api.session.room.TimelineHolder
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
import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineHolder
import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest
import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback
-import org.koin.core.parameter.parametersOf
import org.koin.dsl.context.ModuleDefinition
import org.koin.dsl.module.Module
import org.koin.dsl.module.module
@@ -31,14 +32,16 @@ class RoomModule : Module {
PaginationRequest(get(), get(), get(), get())
}
+
factory {
val roomId: String = it[0]
- TimelineBoundaryCallback(roomId, get(), get(), Executors.newSingleThreadExecutor())
+ val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get(), Executors.newSingleThreadExecutor())
+ DefaultTimelineHolder(roomId, get(), timelineBoundaryCallback) as TimelineHolder
}
factory {
val roomId: String = it[0]
- DefaultTimelineHolder(roomId, get(), get(parameters = { parametersOf(roomId) })) as TimelineHolder
+ DefaultSendService(roomId) 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
new file mode 100644
index 00000000..347445ac
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/DefaultSendService.kt
@@ -0,0 +1,45 @@
+package im.vector.matrix.android.internal.session.room.send
+
+import androidx.work.BackoffPolicy
+import androidx.work.Constraints
+import androidx.work.Data
+import androidx.work.ExistingWorkPolicy
+import androidx.work.NetworkType
+import androidx.work.OneTimeWorkRequestBuilder
+import androidx.work.WorkManager
+import im.vector.matrix.android.api.session.room.SendService
+import im.vector.matrix.android.api.util.Cancelable
+import im.vector.matrix.android.internal.util.CancelableWork
+import java.util.concurrent.TimeUnit
+
+private const val SEND_WORK_NAME = "SEND_WORK_NAME"
+
+internal class DefaultSendService(private val roomId: String) : SendService {
+
+ private val sendConstraints = Constraints.Builder()
+ .setRequiredNetworkType(NetworkType.CONNECTED)
+ .build()
+
+ override fun sendTextMessage(text: String): Cancelable {
+
+ val data = mapOf(
+ "roomId" to roomId,
+ "text" to text
+ )
+ val workData = Data.Builder().putAll(data).build()
+
+ val sendWork = OneTimeWorkRequestBuilder()
+ .setConstraints(sendConstraints)
+ .setInputData(workData)
+ .setBackoffCriteria(BackoffPolicy.LINEAR, 10_000, TimeUnit.MILLISECONDS)
+ .build()
+
+ val work = WorkManager.getInstance()
+ .beginUniqueWork(SEND_WORK_NAME, ExistingWorkPolicy.APPEND, sendWork)
+ .enqueue()
+
+ return CancelableWork(work)
+
+ }
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendContentWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendContentWorker.kt
new file mode 100644
index 00000000..1f972dfe
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendContentWorker.kt
@@ -0,0 +1,37 @@
+package im.vector.matrix.android.internal.session.room.send
+
+import android.content.Context
+import androidx.work.Worker
+import androidx.work.WorkerParameters
+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.network.executeRequest
+import im.vector.matrix.android.internal.session.room.RoomAPI
+import org.koin.standalone.KoinComponent
+import org.koin.standalone.inject
+
+internal class SendContentWorker(context: Context, params: WorkerParameters)
+ : Worker(context, params), KoinComponent {
+
+ private val roomAPI by inject()
+
+ override fun doWork(): Result {
+
+ val roomId = inputData.getString("roomId")
+ val text = inputData.getString("text")
+
+ val fakeId = roomId + "-" + System.currentTimeMillis()
+
+ if (roomId == null || text == null) {
+ return Result.FAILURE
+ }
+
+ val result = executeRequest {
+ apiCall = roomAPI.send(fakeId, roomId, EventType.MESSAGE, MessageContent(MessageType.MSGTYPE_TEXT, text))
+ }
+ return result.fold({ Result.RETRY }, { Result.SUCCESS })
+ }
+
+
+}
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendResponse.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendResponse.kt
new file mode 100644
index 00000000..1f285797
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/send/SendResponse.kt
@@ -0,0 +1,9 @@
+package im.vector.matrix.android.internal.session.room.send
+
+import com.squareup.moshi.Json
+import com.squareup.moshi.JsonClass
+
+@JsonClass(generateAdapter = true)
+internal data class SendResponse(
+ @Json(name = "event_id") val eventId: String
+)
\ No newline at end of file
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableWork.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableWork.kt
new file mode 100644
index 00000000..d78f2045
--- /dev/null
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/util/CancelableWork.kt
@@ -0,0 +1,13 @@
+package im.vector.matrix.android.internal.util
+
+import com.google.common.util.concurrent.ListenableFuture
+import im.vector.matrix.android.api.util.Cancelable
+import kotlinx.coroutines.Job
+
+internal class CancelableWork(private val work: ListenableFuture) : Cancelable {
+
+ override fun cancel() {
+ work.cancel(true)
+ }
+
+}
\ No newline at end of file