diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index 57dc3c02..cf9e857e 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -56,10 +56,12 @@ dependencies { def support_version = '28.0.0' def moshi_version = '1.8.0' def lifecycle_version = "1.1.1" + def coroutines_version = "1.0.1" implementation fileTree(dir: 'libs', include: ['*.aar']) implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" - implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.0.0' + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" + implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation "com.android.support:appcompat-v7:$support_version" implementation "com.android.support:recyclerview-v7:$support_version" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/Task.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/Task.kt new file mode 100644 index 00000000..84c457d7 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/Task.kt @@ -0,0 +1,9 @@ +package im.vector.matrix.android.internal + +import arrow.core.Try + +interface Task { + + fun execute(params: PARAMS): Try + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/TaskExecutor.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/TaskExecutor.kt new file mode 100644 index 00000000..e720a24c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/TaskExecutor.kt @@ -0,0 +1,27 @@ +package im.vector.matrix.android.internal + +import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.util.CancelableCoroutine +import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers +import kotlinx.coroutines.GlobalScope +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber + +internal class TaskExecutor(private val coroutineDispatchers: MatrixCoroutineDispatchers) { + + fun executeTask(task: Task, + params: PARAMS, + callback: MatrixCallback): Cancelable { + val job = GlobalScope.launch(coroutineDispatchers.main) { + val resultOrFailure = withContext(coroutineDispatchers.io) { + Timber.v("Executing ${task.javaClass} on ${Thread.currentThread().name}") + task.execute(params) + } + resultOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) }) + } + return CancelableCoroutine(job) + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt index 154777d0..b0c0634d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt @@ -1,11 +1,10 @@ package im.vector.matrix.android.internal.di import im.vector.matrix.android.api.MatrixOptions -import im.vector.matrix.android.api.thread.MainThreadExecutor +import im.vector.matrix.android.internal.TaskExecutor import im.vector.matrix.android.internal.util.BackgroundDetectionObserver import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.asCoroutineDispatcher import org.koin.dsl.module.module @@ -18,7 +17,11 @@ class MatrixModule(private val options: MatrixOptions) { } single { - MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = MainThreadExecutor().asCoroutineDispatcher()) + MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = Dispatchers.Main) + } + + single { + TaskExecutor(get()) } single { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt similarity index 74% rename from matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataRequest.kt rename to matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt index 47aa3308..d1954956 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/DefaultGetGroupDataTask.kt @@ -5,38 +5,30 @@ import arrow.core.fix import arrow.instances.`try`.monad.monad import arrow.typeclasses.binding import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.Task import im.vector.matrix.android.internal.database.model.GroupSummaryEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.group.model.GroupRooms import im.vector.matrix.android.internal.session.group.model.GroupSummaryResponse import im.vector.matrix.android.internal.session.group.model.GroupUsers -import im.vector.matrix.android.internal.util.CancelableCoroutine -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.tryTransactionSync import io.realm.kotlin.createObject -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -internal class GetGroupDataRequest( +internal interface GetGroupDataTask : Task { + + data class Params(val groupId: String) + +} + + +internal class DefaultGetGroupDataTask( private val groupAPI: GroupAPI, - private val monarchy: Monarchy, - private val coroutineDispatchers: MatrixCoroutineDispatchers -) { + private val monarchy: Monarchy +) : GetGroupDataTask { - fun execute(groupId: String, - callback: MatrixCallback - ): Cancelable { - val job = GlobalScope.launch(coroutineDispatchers.main) { - val groupOrFailure = getGroupData(groupId) - groupOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(true) }) - } - return CancelableCoroutine(job) - } - - fun getGroupData(groupId: String): Try { + override fun execute(params: GetGroupDataTask.Params): Try { + val groupId = params.groupId return Try.monad().binding { val groupSummary = executeRequest { @@ -55,6 +47,7 @@ internal class GetGroupDataRequest( }.fix() } + private fun insertInDb(groupSummary: GroupSummaryResponse, groupRooms: GroupRooms, groupUsers: GroupUsers, @@ -77,7 +70,6 @@ internal class GetGroupDataRequest( groupSummaryEntity.userIds.clear() groupSummaryEntity.userIds.addAll(userIds) - } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt index ce4a8c10..828c883d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GetGroupDataWorker.kt @@ -20,7 +20,7 @@ internal class GetGroupDataWorker(context: Context, val deletionIndexes: List ) - private val getGroupDataRequest by inject() + private val getGroupDataTask by inject() override fun doWork(): Result { val params = WorkerParamsFactory.fromData(inputData) @@ -35,7 +35,7 @@ internal class GetGroupDataWorker(context: Context, } private fun fetchGroupData(groupId: String): Try { - return getGroupDataRequest.getGroupData(groupId) + return getGroupDataTask.execute(GetGroupDataTask.Params(groupId)) } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt index d1dd75ff..30da4c81 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/group/GroupModule.kt @@ -14,7 +14,7 @@ class GroupModule { } scope(DefaultSession.SCOPE) { - GetGroupDataRequest(get(), get(), get()) + DefaultGetGroupDataTask(get(), get()) as GetGroupDataTask } } 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 2fd869b3..c24eb4be 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 @@ -14,12 +14,13 @@ import im.vector.matrix.android.api.session.room.model.Membership 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 +import im.vector.matrix.android.internal.TaskExecutor import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where -import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersRequest +import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask import im.vector.matrix.android.internal.session.sync.SyncTokenStore import org.koin.core.parameter.parametersOf import org.koin.standalone.KoinComponent @@ -30,11 +31,12 @@ internal data class DefaultRoom( override val myMembership: MyMembership ) : Room, KoinComponent { - private val loadRoomMembersRequest by inject() + private val loadRoomMembersTask by inject() private val syncTokenStore by inject() private val monarchy by inject() private val timelineHolder by inject { parametersOf(roomId) } private val sendService by inject { parametersOf(roomId) } + private val taskExecutor by inject() override val roomSummary: LiveData by lazy { val liveData = monarchy @@ -56,7 +58,8 @@ internal data class DefaultRoom( object : Cancelable {} } else { val token = syncTokenStore.getLastToken() - loadRoomMembersRequest.execute(roomId, token, Membership.LEAVE, object : MatrixCallback {}) + val params = LoadRoomMembersTask.Params(roomId, token, Membership.LEAVE) + taskExecutor.executeTask(loadRoomMembersTask, params, object : MatrixCallback {}) } } 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 a823124c..0baf406b 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 @@ -5,9 +5,16 @@ 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.members.DefaultLoadRoomMembersTask +import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.send.DefaultSendService -import im.vector.matrix.android.internal.session.room.timeline.* +import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineHolder +import im.vector.matrix.android.internal.session.room.timeline.DefaultGetContextOfEventTask +import im.vector.matrix.android.internal.session.room.timeline.DefaultPaginationTask +import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask +import im.vector.matrix.android.internal.session.room.timeline.PaginationTask +import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback +import im.vector.matrix.android.internal.session.room.timeline.TokenChunkEventPersistor import im.vector.matrix.android.internal.util.PagingRequestHelper import org.koin.dsl.module.module import retrofit2.Retrofit @@ -24,7 +31,7 @@ class RoomModule { } scope(DefaultSession.SCOPE) { - LoadRoomMembersRequest(get(), get(), get()) + DefaultLoadRoomMembersTask(get(), get()) as LoadRoomMembersTask } scope(DefaultSession.SCOPE) { @@ -32,11 +39,11 @@ class RoomModule { } scope(DefaultSession.SCOPE) { - PaginationRequest(get(), get(), get()) + DefaultPaginationTask(get(), get()) as PaginationTask } scope(DefaultSession.SCOPE) { - GetContextOfEventRequest(get(), get(), get()) + DefaultGetContextOfEventTask(get(), get()) as GetContextOfEventTask } scope(DefaultSession.SCOPE) { @@ -46,8 +53,8 @@ class RoomModule { factory { (roomId: String) -> val helper = PagingRequestHelper(Executors.newSingleThreadExecutor()) - val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get(), helper) - DefaultTimelineHolder(roomId, get(), timelineBoundaryCallback, get()) as TimelineHolder + val timelineBoundaryCallback = TimelineBoundaryCallback(roomId, get(), get(), get(), helper) + DefaultTimelineHolder(roomId, get(), get() , timelineBoundaryCallback, get()) as TimelineHolder } factory { (roomId: String) -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt deleted file mode 100644 index c3fca61d..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersRequest.kt +++ /dev/null @@ -1,64 +0,0 @@ -package im.vector.matrix.android.internal.session.room.members - -import arrow.core.Try -import com.zhuinden.monarchy.Monarchy -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.session.room.model.Membership -import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.database.helper.addStateEvents -import im.vector.matrix.android.internal.database.model.RoomEntity -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.CancelableCoroutine -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import im.vector.matrix.android.internal.util.tryTransactionSync -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -internal class LoadRoomMembersRequest(private val roomAPI: RoomAPI, - private val monarchy: Monarchy, - private val coroutineDispatchers: MatrixCoroutineDispatchers) { - - fun execute(roomId: String, - streamToken: String?, - excludeMembership: Membership? = null, - callback: MatrixCallback - ): Cancelable { - val job = GlobalScope.launch(coroutineDispatchers.main) { - val responseOrFailure = execute(roomId, streamToken, excludeMembership) - responseOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(true) }) - } - return CancelableCoroutine(job) - } - - //TODO : manage stream token (we have 404 on some rooms actually) - private suspend fun execute(roomId: String, - streamToken: String?, - excludeMembership: Membership?) = withContext(coroutineDispatchers.io) { - - executeRequest { - apiCall = roomAPI.getMembers(roomId, null, null, excludeMembership?.value) - }.flatMap { response -> - insertInDb(response, roomId) - } - } - - private fun insertInDb(response: RoomMembersResponse, roomId: String): Try { - return monarchy - .tryTransactionSync { realm -> - // We ignore all the already known members - val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: throw IllegalStateException("You shouldn't use this method without a room") - - val roomMembers = RoomMembers(realm, roomId).getLoaded() - val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) } - - roomEntity.addStateEvents(eventsToInsert) - roomEntity.areAllMembersLoaded = true - } - .map { response } - } - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersTask.kt new file mode 100644 index 00000000..04ebaa19 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/members/LoadRoomMembersTask.kt @@ -0,0 +1,51 @@ +package im.vector.matrix.android.internal.session.room.members + +import arrow.core.Try +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.room.model.Membership +import im.vector.matrix.android.internal.Task +import im.vector.matrix.android.internal.database.helper.addStateEvents +import im.vector.matrix.android.internal.database.model.RoomEntity +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.tryTransactionSync + +internal interface LoadRoomMembersTask : Task { + + data class Params( + val roomId: String, + val streamToken: String?, + val excludeMembership: Membership? = null + ) +} + +internal class DefaultLoadRoomMembersTask(private val roomAPI: RoomAPI, + private val monarchy: Monarchy +) : LoadRoomMembersTask { + + override fun execute(params: LoadRoomMembersTask.Params): Try { + return executeRequest { + apiCall = roomAPI.getMembers(params.roomId, null, null, params.excludeMembership?.value) + }.flatMap { response -> + insertInDb(response, params.roomId) + }.map { true } + } + + private fun insertInDb(response: RoomMembersResponse, roomId: String): Try { + return monarchy + .tryTransactionSync { realm -> + // We ignore all the already known members + val roomEntity = RoomEntity.where(realm, roomId).findFirst() + ?: throw IllegalStateException("You shouldn't use this method without a room") + + val roomMembers = RoomMembers(realm, roomId).getLoaded() + val eventsToInsert = response.roomMemberEvents.filter { !roomMembers.containsKey(it.stateKey) } + + roomEntity.addStateEvents(eventsToInsert) + roomEntity.areAllMembersLoaded = true + } + .map { response } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt new file mode 100644 index 00000000..c4fc4831 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultGetContextOfEventTask.kt @@ -0,0 +1,31 @@ +package im.vector.matrix.android.internal.session.room.timeline + +import arrow.core.Try +import im.vector.matrix.android.internal.Task +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.util.FilterUtil + +internal interface GetContextOfEventTask : Task { + + data class Params( + val roomId: String, + val eventId: String + ) + +} + +internal class DefaultGetContextOfEventTask(private val roomAPI: RoomAPI, + private val tokenChunkEventPersistor: TokenChunkEventPersistor +) : GetContextOfEventTask { + + override fun execute(params: GetContextOfEventTask.Params): Try { + val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() + return executeRequest { + apiCall = roomAPI.getContextOfEvent(params.roomId, params.eventId, 0, filter) + }.flatMap { response -> + tokenChunkEventPersistor.insertInDb(response, params.roomId, PaginationDirection.BACKWARDS).map { response } + } + } + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt new file mode 100644 index 00000000..30f7a32e --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultPaginationTask.kt @@ -0,0 +1,39 @@ +package im.vector.matrix.android.internal.session.room.timeline + +import arrow.core.Try +import arrow.core.failure +import im.vector.matrix.android.internal.Task +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.room.RoomAPI +import im.vector.matrix.android.internal.util.FilterUtil + + +internal interface PaginationTask : Task { + + data class Params( + val roomId: String, + val from: String?, + val direction: PaginationDirection, + val limit: Int + ) + +} + +internal class DefaultPaginationTask(private val roomAPI: RoomAPI, + private val tokenChunkEventPersistor: TokenChunkEventPersistor +) : PaginationTask { + + override fun execute(params: PaginationTask.Params): Try { + if (params.from == null) { + return RuntimeException("From token shouldn't be null").failure() + } + val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() + return executeRequest { + apiCall = roomAPI.getRoomMessagesFrom(params.roomId, params.from, params.direction.value, params.limit, filter) + }.flatMap { chunk -> + tokenChunkEventPersistor + .insertInDb(chunk, params.roomId, params.direction) + .map { chunk } + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt index ba7acc2a..a78297c2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/DefaultTimelineHolder.kt @@ -8,6 +8,7 @@ import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.room.TimelineHolder +import im.vector.matrix.android.internal.TaskExecutor import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.ChunkEntityFields import im.vector.matrix.android.internal.database.model.EventEntity @@ -22,14 +23,14 @@ private const val PAGE_SIZE = 30 internal class DefaultTimelineHolder(private val roomId: String, private val monarchy: Monarchy, + private val taskExecutor: TaskExecutor, private val boundaryCallback: TimelineBoundaryCallback, - private val contextOfEventRequest: GetContextOfEventRequest + private val contextOfEventTask: GetContextOfEventTask ) : TimelineHolder { private val eventInterceptors = ArrayList() init { - boundaryCallback.limit = 30 eventInterceptors.add(MessageEventInterceptor(monarchy, roomId)) } @@ -76,7 +77,8 @@ internal class DefaultTimelineHolder(private val roomId: String, private fun fetchEventIfNeeded(eventId: String) { if (!isEventPersisted(eventId)) { - contextOfEventRequest.execute(roomId, eventId, object : MatrixCallback {}) + val params = GetContextOfEventTask.Params(roomId, eventId) + taskExecutor.executeTask(contextOfEventTask, params, object : MatrixCallback {}) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt deleted file mode 100644 index f4f792d1..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetContextOfEventRequest.kt +++ /dev/null @@ -1,43 +0,0 @@ -package im.vector.matrix.android.internal.session.room.timeline - -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.util.CancelableCoroutine -import im.vector.matrix.android.internal.util.FilterUtil -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -internal class GetContextOfEventRequest(private val roomAPI: RoomAPI, - private val tokenChunkEventPersistor: TokenChunkEventPersistor, - private val coroutineDispatchers: MatrixCoroutineDispatchers -) { - - fun execute(roomId: String, - eventId: String, - callback: MatrixCallback - ): Cancelable { - val job = GlobalScope.launch(coroutineDispatchers.main) { - val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() - val contextOrFailure = execute(roomId, eventId, filter) - contextOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) }) - } - return CancelableCoroutine(job) - } - - private suspend fun execute(roomId: String, - eventId: String, - filter: String?) = withContext(coroutineDispatchers.io) { - - executeRequest { - apiCall = roomAPI.getContextOfEvent(roomId, eventId, 0, filter) - }.flatMap { response -> - tokenChunkEventPersistor.insertInDb(response, roomId, PaginationDirection.BACKWARDS).map { response } - } - } - - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventRequest.kt deleted file mode 100644 index 28abd072..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventRequest.kt +++ /dev/null @@ -1,39 +0,0 @@ -package im.vector.matrix.android.internal.session.room.timeline - -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.util.Cancelable -import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.util.CancelableCoroutine -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -internal class GetEventRequest(private val roomAPI: RoomAPI, - private val monarchy: Monarchy, - private val coroutineDispatchers: MatrixCoroutineDispatchers -) { - - fun execute(roomId: String, - eventId: String, - callback: MatrixCallback - ): Cancelable { - val job = GlobalScope.launch(coroutineDispatchers.main) { - val eventOrFailure = execute(roomId, eventId) - eventOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) }) - } - return CancelableCoroutine(job) - } - - private suspend fun execute(roomId: String, - eventId: String) = withContext(coroutineDispatchers.io) { - - executeRequest { - apiCall = roomAPI.getEvent(roomId, eventId) - } - } - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt new file mode 100644 index 00000000..596d5801 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/GetEventTask.kt @@ -0,0 +1,22 @@ +package im.vector.matrix.android.internal.session.room.timeline + +import arrow.core.Try +import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.internal.Task +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.room.RoomAPI + +internal class GetEventTask(private val roomAPI: RoomAPI +) : Task { + + internal data class Params( + val roomId: String, + val eventId: String + ) + + override fun execute(params: Params): Try { + return executeRequest { + apiCall = roomAPI.getEvent(params.roomId, params.eventId) + } + } +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt deleted file mode 100644 index e94d0387..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/PaginationRequest.kt +++ /dev/null @@ -1,52 +0,0 @@ -package im.vector.matrix.android.internal.session.room.timeline - -import arrow.core.failure -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.room.RoomAPI -import im.vector.matrix.android.internal.util.CancelableCoroutine -import im.vector.matrix.android.internal.util.FilterUtil -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -internal class PaginationRequest(private val roomAPI: RoomAPI, - private val tokenChunkEventPersistor: TokenChunkEventPersistor, - private val coroutineDispatchers: MatrixCoroutineDispatchers -) { - - fun execute(roomId: String, - from: String?, - direction: PaginationDirection, - limit: Int, - callback: MatrixCallback - ): Cancelable { - val job = GlobalScope.launch(coroutineDispatchers.main) { - val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() - val chunkOrFailure = execute(roomId, from, direction, limit, filter) - chunkOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) }) - } - return CancelableCoroutine(job) - } - - private suspend fun execute(roomId: String, - from: String?, - direction: PaginationDirection, - limit: Int, - filter: String?) = withContext(coroutineDispatchers.io) { - - if (from == null) { - return@withContext RuntimeException("From token shouldn't be null").failure() - } - executeRequest { - apiCall = roomAPI.getRoomMessagesFrom(roomId, from, direction.value, limit, filter) - }.flatMap { chunk -> - tokenChunkEventPersistor - .insertInDb(chunk, roomId, direction) - .map { chunk } - } - } - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt index bab802e2..623237a7 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/timeline/TimelineBoundaryCallback.kt @@ -4,18 +4,20 @@ 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.internal.TaskExecutor import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.query.findAllIncludingEvents import im.vector.matrix.android.internal.util.PagingRequestHelper import java.util.* internal class TimelineBoundaryCallback(private val roomId: String, - private val paginationRequest: PaginationRequest, + private val taskExecutor: TaskExecutor, + private val paginationTask: PaginationTask, private val monarchy: Monarchy, private val helper: PagingRequestHelper ) : PagedList.BoundaryCallback() { - var limit = 10 + var limit = 30 override fun onZeroItemsLoaded() { // actually, it's not possible @@ -44,13 +46,12 @@ internal class TimelineBoundaryCallback(private val roomId: String, val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(item.root.eventId)).firstOrNull() token = if (direction == PaginationDirection.FORWARDS) chunkEntity?.nextToken else chunkEntity?.prevToken } - paginationRequest.execute( - roomId = roomId, - from = token, - direction = direction, - limit = limit, - callback = createCallback(requestCallback) - ) + val params = PaginationTask.Params(roomId = roomId, + from = token, + direction = direction, + limit = limit) + + taskExecutor.executeTask(paginationTask, params, createCallback(requestCallback)) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt index dca339c0..17634f6d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncModule.kt @@ -36,7 +36,7 @@ internal class SyncModule { } scope(DefaultSession.SCOPE) { - SyncRequest(get(), get(), get()) + DefaultSyncTask(get(), get()) as SyncTask } scope(DefaultSession.SCOPE) { @@ -44,7 +44,7 @@ internal class SyncModule { } scope(DefaultSession.SCOPE) { - SyncThread(get(), get(), get(), get()) + SyncThread(get(), get(), get(), get(), get()) } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncRequest.kt deleted file mode 100644 index d3348dea..00000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncRequest.kt +++ /dev/null @@ -1,46 +0,0 @@ -package im.vector.matrix.android.internal.session.sync - -import im.vector.matrix.android.api.MatrixCallback -import im.vector.matrix.android.api.util.Cancelable -import im.vector.matrix.android.internal.network.executeRequest -import im.vector.matrix.android.internal.session.filter.FilterBody -import im.vector.matrix.android.internal.session.sync.model.SyncResponse -import im.vector.matrix.android.internal.util.CancelableCoroutine -import im.vector.matrix.android.internal.util.FilterUtil -import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext - -internal class SyncRequest(private val syncAPI: SyncAPI, - private val coroutineDispatchers: MatrixCoroutineDispatchers, - private val syncResponseHandler: SyncResponseHandler) { - - - fun execute(token: String?, callback: MatrixCallback): Cancelable { - val job = GlobalScope.launch { - val syncOrFailure = execute(token) - syncOrFailure.fold({ callback.onFailure(it) }, { callback.onSuccess(it) }) - } - return CancelableCoroutine(job) - } - - private suspend fun execute(token: String?) = withContext(coroutineDispatchers.io) { - val params = HashMap() - val filterBody = FilterBody() - FilterUtil.enableLazyLoading(filterBody, true) - var timeout = 0 - if (token != null) { - params["since"] = token - timeout = 30000 - } - params["timeout"] = timeout.toString() - params["filter"] = filterBody.toJSONString() - executeRequest { - apiCall = syncAPI.sync(params) - }.flatMap { syncResponse -> - syncResponseHandler.handleResponse(syncResponse, token, false) - } - } - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt new file mode 100644 index 00000000..533019d3 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/SyncTask.kt @@ -0,0 +1,41 @@ +package im.vector.matrix.android.internal.session.sync + +import arrow.core.Try +import im.vector.matrix.android.internal.Task +import im.vector.matrix.android.internal.network.executeRequest +import im.vector.matrix.android.internal.session.filter.FilterBody +import im.vector.matrix.android.internal.session.sync.model.SyncResponse +import im.vector.matrix.android.internal.util.FilterUtil + +internal interface SyncTask : Task { + + data class Params(val token: String?) + +} + +internal class DefaultSyncTask(private val syncAPI: SyncAPI, + private val syncResponseHandler: SyncResponseHandler +) : SyncTask { + + + override fun execute(params: SyncTask.Params): Try { + val requestParams = HashMap() + val filterBody = FilterBody() + FilterUtil.enableLazyLoading(filterBody, true) + var timeout = 0 + if (params.token != null) { + requestParams["since"] = params.token + timeout = 30000 + } + requestParams["timeout"] = timeout.toString() + requestParams["filter"] = filterBody.toJSONString() + + return executeRequest { + apiCall = syncAPI.sync(requestParams) + }.flatMap { syncResponse -> + syncResponseHandler.handleResponse(syncResponse, params.token, false) + } + } + + +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt index 67617bfb..0a523a13 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/job/SyncThread.kt @@ -3,8 +3,9 @@ package im.vector.matrix.android.internal.session.sync.job import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.util.Cancelable +import im.vector.matrix.android.internal.TaskExecutor import im.vector.matrix.android.internal.network.NetworkConnectivityChecker -import im.vector.matrix.android.internal.session.sync.SyncRequest +import im.vector.matrix.android.internal.session.sync.SyncTask import im.vector.matrix.android.internal.session.sync.SyncTokenStore import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.util.BackgroundDetectionObserver @@ -13,10 +14,11 @@ import java.util.concurrent.CountDownLatch private const val RETRY_WAIT_TIME_MS = 10_000L -internal class SyncThread(private val syncRequest: SyncRequest, +internal class SyncThread(private val syncTask: SyncTask, private val networkConnectivityChecker: NetworkConnectivityChecker, private val syncTokenStore: SyncTokenStore, - private val backgroundDetectionObserver: BackgroundDetectionObserver + private val backgroundDetectionObserver: BackgroundDetectionObserver, + private val taskExecutor: TaskExecutor ) : Thread(), NetworkConnectivityChecker.Listener, BackgroundDetectionObserver.Listener { enum class State { @@ -30,7 +32,7 @@ internal class SyncThread(private val syncRequest: SyncRequest, private var state: State = State.IDLE private val lock = Object() private var nextBatch = syncTokenStore.getLastToken() - private var cancelableRequest: Cancelable? = null + private var cancelableTask: Cancelable? = null fun restart() { synchronized(lock) { @@ -57,7 +59,7 @@ internal class SyncThread(private val syncRequest: SyncRequest, synchronized(lock) { Timber.v("Kill sync...") state = State.KILLING - cancelableRequest?.cancel() + cancelableTask?.cancel() lock.notify() } } @@ -77,7 +79,8 @@ internal class SyncThread(private val syncRequest: SyncRequest, } else { Timber.v("Execute sync request...") val latch = CountDownLatch(1) - cancelableRequest = syncRequest.execute(nextBatch, object : MatrixCallback { + val params = SyncTask.Params(nextBatch) + cancelableTask = taskExecutor.executeTask(syncTask, params, object : MatrixCallback { override fun onSuccess(data: SyncResponse) { nextBatch = data.nextBatch syncTokenStore.saveToken(nextBatch)