Start sending message : introduce WorkManager. WIP - have to clean

This commit is contained in:
ganfra 2018-11-08 19:08:14 +01:00
parent b2bb89ac94
commit f050574728
19 changed files with 207 additions and 11 deletions

View file

@ -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() {

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 257 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 423 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 728 B

View file

@ -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" />
<View
android:id="@+id/composerDivider"
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/pale_grey"
app:layout_constraintBottom_toTopOf="@+id/composerLayout" />
<RelativeLayout
android:id="@+id/composerLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageButton
android:id="@+id/sendButton"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentEnd="true"
android:background="?android:attr/selectableItemBackground"
android:src="@drawable/ic_send_white"
android:tint="?attr/colorAccent"
android:layout_alignParentRight="true"
android:layout_centerVertical="true" />
<EditText
android:id="@+id/composerEditText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@id/sendButton"
android:background="@android:color/transparent"
android:gravity="center_vertical"
android:hint="Send a message"
android:minHeight="48dp"
android:nextFocusLeft="@id/composerEditText"
android:nextFocusUp="@id/composerEditText"
android:padding="16dp"
android:textSize="14sp" />
</RelativeLayout>
</android.support.constraint.ConstraintLayout>

View file

@ -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"

View file

@ -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<String, Any>
@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<String, Any>? = null,
@Json(name = "prev_content") val prevContent: Map<String, Any>? = 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 <reified T> toModel(data: Map<String, Any>?): T? {
inline fun <reified T> toModel(data: Content?): T? {
val moshi = MoshiProvider.providesMoshi()
val moshiAdapter = moshi.adapter(T::class.java)
return moshiAdapter.fromJsonValue(data)

View file

@ -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

View file

@ -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
}

View file

@ -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<LoadRoomMembersRequest>()
private val syncTokenStore by inject<SyncTokenStore>()
private val monarchy by inject<Monarchy>()
private val timelineHolder by inject<TimelineHolder>(parameters = { parametersOf(roomId) })
private val sendService by inject<SendService>(parameters = { parametersOf(roomId) })
override val roomSummary: LiveData<RoomSummary> by lazy {
val liveData = monarchy
@ -66,4 +67,8 @@ internal data class DefaultRoom(
}
override fun sendTextMessage(text: String): Cancelable {
return sendService.sendTextMessage(text)
}
}

View file

@ -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<RoomMembersResponse>
/**
* 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<SendResponse>
}

View file

@ -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()

View file

@ -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<SendContentWorker>()
.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)
}
}

View file

@ -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<RoomAPI>()
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<SendResponse> {
apiCall = roomAPI.send(fakeId, roomId, EventType.MESSAGE, MessageContent(MessageType.MSGTYPE_TEXT, text))
}
return result.fold({ Result.RETRY }, { Result.SUCCESS })
}
}

View file

@ -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
)

View file

@ -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<Void>) : Cancelable {
override fun cancel() {
work.cancel(true)
}
}