diff --git a/changelog.d/6463.bugfix b/changelog.d/6463.bugfix new file mode 100644 index 0000000000..63d66d018e --- /dev/null +++ b/changelog.d/6463.bugfix @@ -0,0 +1 @@ +Fix crashes when opening Thread diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt index d1eb8794bf..4eaac67e5a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/DefaultTimeline.kt @@ -20,6 +20,7 @@ import io.realm.Realm import io.realm.RealmConfiguration import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.android.asCoroutineDispatcher import kotlinx.coroutines.cancelChildren @@ -116,6 +117,7 @@ internal class DefaultTimeline( ) private var strategy: LoadTimelineStrategy = buildStrategy(LoadTimelineStrategy.Mode.Live) + private var startTimelineJob: Job? = null override val isLive: Boolean get() = !getPaginationState(Timeline.Direction.FORWARDS).hasMoreToLoad @@ -143,7 +145,7 @@ internal class DefaultTimeline( timelineScope.launch { loadRoomMembersIfNeeded() } - timelineScope.launch { + startTimelineJob = timelineScope.launch { sequencer.post { if (isStarted.compareAndSet(false, true)) { isFromThreadTimeline = rootThreadEventId != null @@ -174,8 +176,10 @@ internal class DefaultTimeline( override fun restartWithEventId(eventId: String?) { timelineScope.launch { - openAround(eventId, rootThreadEventId) - postSnapshot() + sequencer.post { + openAround(eventId, rootThreadEventId) + postSnapshot() + } } } @@ -185,6 +189,7 @@ internal class DefaultTimeline( override fun paginate(direction: Timeline.Direction, count: Int) { timelineScope.launch { + startTimelineJob?.join() val postSnapshot = loadMore(count, direction, fetchOnServerIfNeeded = true) if (postSnapshot) { postSnapshot() @@ -193,6 +198,7 @@ internal class DefaultTimeline( } override suspend fun awaitPaginate(direction: Timeline.Direction, count: Int): List { + startTimelineJob?.join() withContext(timelineDispatcher) { loadMore(count, direction, fetchOnServerIfNeeded = true) } @@ -279,6 +285,7 @@ internal class DefaultTimeline( direction = Timeline.Direction.BACKWARDS, fetchOnServerIfNeeded = false ) + Timber.v("$baseLogMessage finished") } @@ -312,9 +319,11 @@ internal class DefaultTimeline( private fun onLimitedTimeline() { timelineScope.launch { - initPaginationStates(null) - loadMore(settings.initialSize, Timeline.Direction.BACKWARDS, false) - postSnapshot() + sequencer.post { + initPaginationStates(null) + loadMore(settings.initialSize, Timeline.Direction.BACKWARDS, false) + postSnapshot() + } } } diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt index c5d4d346fd..2d50fcb67a 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/session/room/timeline/LoadTimelineStrategy.kt @@ -32,6 +32,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import org.matrix.android.sdk.api.session.room.timeline.TimelineSettings import org.matrix.android.sdk.api.settings.LightweightSettingsStorage +import org.matrix.android.sdk.internal.database.awaitTransaction import org.matrix.android.sdk.internal.database.helper.addIfNecessary import org.matrix.android.sdk.internal.database.mapper.TimelineEventMapper import org.matrix.android.sdk.internal.database.model.ChunkEntity @@ -265,7 +266,7 @@ internal class LoadTimelineStrategy constructor( } } - private fun getChunkEntity(realm: Realm): RealmResults { + private suspend fun getChunkEntity(realm: Realm): RealmResults { return when (mode) { is Mode.Live -> { ChunkEntity.where(realm, roomId) @@ -289,8 +290,8 @@ internal class LoadTimelineStrategy constructor( * Clear any existing thread chunk entity and create a new one, with the * rootThreadEventId included. */ - private fun recreateThreadChunkEntity(realm: Realm, rootThreadEventId: String) { - realm.executeTransaction { + private suspend fun recreateThreadChunkEntity(realm: Realm, rootThreadEventId: String) { + awaitTransaction(realm.configuration) { // Lets delete the chunk and start a new one ChunkEntity.findLastForwardChunkOfThread(it, roomId, rootThreadEventId)?.deleteAndClearThreadEvents()?.let { Timber.i("###THREADS LoadTimelineStrategy [onStart] thread chunk cleared..") @@ -309,8 +310,8 @@ internal class LoadTimelineStrategy constructor( /** * Clear any existing thread chunk. */ - private fun clearThreadChunkEntity(realm: Realm, rootThreadEventId: String) { - realm.executeTransaction { + private suspend fun clearThreadChunkEntity(realm: Realm, rootThreadEventId: String) { + awaitTransaction(realm.configuration) { ChunkEntity.findLastForwardChunkOfThread(it, roomId, rootThreadEventId)?.deleteAndClearThreadEvents()?.let { Timber.i("###THREADS LoadTimelineStrategy [onStop] thread chunk cleared..") }