From b91adf937623c6b12da1661e0aa92f3aa330d25c Mon Sep 17 00:00:00 2001 From: ganfra Date: Mon, 17 Jan 2022 19:16:32 +0100 Subject: [PATCH] Timeline : fix 4959 --- changelog.d/4959.bugfix | 1 + .../detail/timeline/TimelineEventController.kt | 16 ++++++++++++++-- 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 changelog.d/4959.bugfix diff --git a/changelog.d/4959.bugfix b/changelog.d/4959.bugfix new file mode 100644 index 00000000000..6ffa3937f93 --- /dev/null +++ b/changelog.d/4959.bugfix @@ -0,0 +1 @@ +Prevent crash in Timeline and add more logs. \ No newline at end of file diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt index 241ccb7428d..bf0c5ee062c 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/TimelineEventController.kt @@ -72,6 +72,7 @@ import org.matrix.android.sdk.api.session.room.timeline.Timeline import org.matrix.android.sdk.api.session.room.timeline.TimelineEvent import timber.log.Timber import javax.inject.Inject +import kotlin.math.min import kotlin.system.measureTimeMillis class TimelineEventController @Inject constructor(private val dateFormatter: VectorDateFormatter, @@ -185,6 +186,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onChanged(position: Int, count: Int, payload: Any?) { synchronized(modelCache) { assertUpdateCallbacksAllowed() + Timber.v("listUpdateCallback.onChanged(position: $position, count: $count). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items") (position until position + count).forEach { // Invalidate cache modelCache[it] = null @@ -192,10 +195,12 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec // Also invalidate the first previous displayable event if // it's sent by the same user so we are sure we have up to date information. val invalidatedSenderId: String? = currentSnapshot.getOrNull(position)?.senderInfo?.userId - val prevDisplayableEventIndex = currentSnapshot.subList(0, position).indexOfLast { + // In some cases onChanged will be called before onRemoved and onInserted so position will be smaller than currentSnapshot.size. + val prevList = currentSnapshot.subList(0, min(position, currentSnapshot.size)) + val prevDisplayableEventIndex = prevList.indexOfLast { timelineEventVisibilityHelper.shouldShowEvent(it, partialState.highlightedEventId) } - if (prevDisplayableEventIndex != -1 && currentSnapshot[prevDisplayableEventIndex].senderInfo.userId == invalidatedSenderId) { + if (prevDisplayableEventIndex != -1 && currentSnapshot.getOrNull(prevDisplayableEventIndex)?.senderInfo?.userId == invalidatedSenderId) { modelCache[prevDisplayableEventIndex] = null } requestModelBuild() @@ -205,6 +210,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onMoved(fromPosition: Int, toPosition: Int) { synchronized(modelCache) { assertUpdateCallbacksAllowed() + Timber.v("listUpdateCallback.onMoved(fromPosition: $fromPosition, toPosition: $toPosition). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items") val model = modelCache.removeAt(fromPosition) modelCache.add(toPosition, model) requestModelBuild() @@ -214,6 +221,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onInserted(position: Int, count: Int) { synchronized(modelCache) { assertUpdateCallbacksAllowed() + Timber.v("listUpdateCallback.onInserted(position: $position, count: $count). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items") repeat(count) { modelCache.add(position, null) } @@ -224,6 +233,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec override fun onRemoved(position: Int, count: Int) { synchronized(modelCache) { assertUpdateCallbacksAllowed() + Timber.v("listUpdateCallback.onRemoved(position: $position, count: $count). " + + "\ncurrentSnapshot has size of ${currentSnapshot.size} items") repeat(count) { modelCache.removeAt(position) } @@ -306,6 +317,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Vec inSubmitList = true val diffCallback = TimelineEventDiffUtilCallback(currentSnapshot, newSnapshot) currentSnapshot = newSnapshot + Timber.v("Submit a new snapshot of ${currentSnapshot.size} items.") val diffResult = DiffUtil.calculateDiff(diffCallback) diffResult.dispatchUpdatesTo(listUpdateCallback) requestDelayedModelBuild(0)