diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 15da4cd7..f980c1ea 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/RoomDetailFragment.kt b/app/src/main/java/im/vector/riotredesign/features/home/RoomDetailFragment.kt index 8d601a60..81b95dc3 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/RoomDetailFragment.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/RoomDetailFragment.kt @@ -17,7 +17,7 @@ import im.vector.riotredesign.core.utils.FragmentArgumentDelegate import kotlinx.android.synthetic.main.fragment_room_detail.* import org.koin.android.ext.android.inject -class RoomDetailFragment : RiotFragment(), TimelineAdapter.Callback { +class RoomDetailFragment : RiotFragment(), TimelineEventAdapter.Callback { companion object { @@ -31,7 +31,8 @@ class RoomDetailFragment : RiotFragment(), TimelineAdapter.Callback { private val matrix by inject() private val currentSession = matrix.currentSession!! private var roomId by FragmentArgumentDelegate() - private val adapter = TimelineAdapter(this) + private val timelineAdapter = TimelineEventAdapter(this) + private val timelineEventController = TimelineEventController() private lateinit var room: Room override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -46,26 +47,27 @@ class RoomDetailFragment : RiotFragment(), TimelineAdapter.Callback { } private fun renderEvents(events: PagedList?) { - adapter.submitList(events) + timelineAdapter.submitList(events) } private fun setupRecyclerView() { - val layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false) - adapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { + val layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, true) + layoutManager.stackFromEnd = true + timelineAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { - if (layoutManager.findLastCompletelyVisibleItemPosition() == positionStart - itemCount) { - layoutManager.scrollToPosition(adapter.itemCount - 1) + if (layoutManager.findFirstVisibleItemPosition() == 0) { + layoutManager.scrollToPosition(0) } } }) recyclerView.layoutManager = layoutManager - recyclerView.adapter = adapter - recyclerView.setHasFixedSize(true) + recyclerView.adapter = timelineAdapter + //recyclerView.setController(timelineEventController) } override fun onEventsListChanged(oldList: List?, newList: List?) { if (oldList == null && newList != null) { - recyclerView.scrollToPosition(newList.size - 1) + recyclerView.scrollToPosition(0) } } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/TimelineAdapter.kt b/app/src/main/java/im/vector/riotredesign/features/home/TimelineEventAdapter.kt similarity index 90% rename from app/src/main/java/im/vector/riotredesign/features/home/TimelineAdapter.kt rename to app/src/main/java/im/vector/riotredesign/features/home/TimelineEventAdapter.kt index f8c3a39e..a3c1b9d0 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/TimelineAdapter.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/TimelineEventAdapter.kt @@ -14,8 +14,8 @@ import im.vector.riotredesign.R * Created by francois on 14/05/2018. */ -class TimelineAdapter(private val callback: Callback? = null) - : PagedListAdapter(EventDiffUtilCallback()) { +class TimelineEventAdapter(private val callback: Callback? = null) + : PagedListAdapter(EventDiffUtilCallback()) { private var currentList: List? = null diff --git a/app/src/main/res/layout/fragment_room_detail.xml b/app/src/main/res/layout/fragment_room_detail.xml index 7d8b00af..0ce705a9 100644 --- a/app/src/main/res/layout/fragment_room_detail.xml +++ b/app/src/main/res/layout/fragment_room_detail.xml @@ -3,7 +3,7 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt index c717ce32..e763893f 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/ChunkEntity.kt @@ -7,7 +7,6 @@ import io.realm.annotations.LinkingObjects open class ChunkEntity(var prevToken: String? = null, var nextToken: String? = null, - var isLimited: Boolean = true, var events: RealmList = RealmList() ) : RealmObject() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt index b722f647..5c877cf4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt @@ -33,7 +33,7 @@ fun ChunkEntity.Companion.findWithNextToken(realm: Realm, roomId: String, nextTo .findFirst() } -fun ChunkEntity.Companion.findLastFromRoom(realm: Realm, roomId: String): ChunkEntity? { +fun ChunkEntity.Companion.findLastLiveChunkFromRoom(realm: Realm, roomId: String): ChunkEntity? { return where(realm, roomId) .and() .isNull("nextToken") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt index 1c980a91..66ed29f4 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -5,6 +5,7 @@ import im.vector.matrix.android.internal.database.model.EventEntity import io.realm.Realm import io.realm.RealmQuery import io.realm.RealmResults +import io.realm.Sort fun EventEntity.Companion.where(realm: Realm, roomId: String): RealmQuery { return realm.where(EventEntity::class.java) @@ -23,9 +24,9 @@ fun EventEntity.Companion.where(realm: Realm, chunk: ChunkEntity?): RealmQuery.getLast(type: String? = null): EventEntity? { - var query = this.where() + var query = this.where().sort("originServerTs", Sort.DESCENDING) if (type != null) { query = query.equalTo("type", type) } - return query.findAll().last(null) + return query.findFirst() } \ 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 c213856c..19c8b5f0 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 @@ -11,6 +11,7 @@ import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback +import io.realm.Sort import org.koin.standalone.KoinComponent import org.koin.standalone.inject import java.util.concurrent.Executors @@ -28,17 +29,16 @@ data class DefaultRoom( ChunkEntity.where(realm, roomId) .findAll() .last(null) - ?.let { it.events } - ?.where() - ?.sort("originServerTs") + ?.let { + it.events.where().sort("originServerTs", Sort.DESCENDING) + } } val domainSourceFactory = realmDataSourceFactory.map { EventMapper.map(it) } val pagedListConfig = PagedList.Config.Builder() .setEnablePlaceholders(false) .setPageSize(10) - .setInitialLoadSizeHint(30) - .setPrefetchDistance(5) + .setPrefetchDistance(10) .build() val livePagedListBuilder = LivePagedListBuilder(domainSourceFactory, pagedListConfig).setBoundaryCallback(boundaryCallback) 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 index 199ea536..74f21182 100644 --- 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 @@ -7,6 +7,7 @@ import com.zhuinden.monarchy.Monarchy 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.database.DBConstants import im.vector.matrix.android.internal.database.mapper.asEntity import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.RoomEntity @@ -14,6 +15,7 @@ import im.vector.matrix.android.internal.database.query.findAllIncludingEvents import im.vector.matrix.android.internal.database.query.findWithNextToken import im.vector.matrix.android.internal.database.query.findWithPrevToken import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.legacy.util.FilterUtil import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.session.room.RoomAPI import im.vector.matrix.android.internal.session.room.model.PaginationDirection @@ -32,10 +34,10 @@ class PaginationRequest(private val roomAPI: RoomAPI, from: String?, direction: PaginationDirection, limit: Int = 10, - filter: String? = null, callback: MatrixCallback ): Cancelable { val job = GlobalScope.launch(coroutineDispatchers.main) { + val filter = FilterUtil.createRoomEventFilter(true)?.toJSONString() val chunkOrFailure = execute(roomId, from, direction, limit, filter) chunkOrFailure.bimap({ callback.onFailure(it) }, { callback.onSuccess(it) }) } @@ -70,14 +72,13 @@ class PaginationRequest(private val roomAPI: RoomAPI, private fun insertInDb(chunkEvent: TokenChunkEvent, roomId: String) { monarchy.runTransactionSync { realm -> val roomEntity = RoomEntity.where(realm, roomId).findFirst() - ?: return@runTransactionSync + ?: return@runTransactionSync val nextChunk = ChunkEntity.findWithPrevToken(realm, roomId, chunkEvent.nextToken) val prevChunk = ChunkEntity.findWithNextToken(realm, roomId, chunkEvent.prevToken) - val mergedEvents = chunkEvent.chunk + chunkEvent.stateEvents - val mergedEventIds = mergedEvents.filter { it.eventId != null }.map { it.eventId!! } - val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, mergedEventIds) + val eventIds = chunkEvent.chunk.filter { it.eventId != null }.map { it.eventId!! } + val chunksOverlapped = ChunkEntity.findAllIncludingEvents(realm, eventIds) val hasOverlapped = chunksOverlapped.isNotEmpty() val currentChunk = if (nextChunk != null) { @@ -85,9 +86,25 @@ class PaginationRequest(private val roomAPI: RoomAPI, } else { ChunkEntity() } - currentChunk.prevToken = chunkEvent.prevToken - mergedEvents.forEach { event -> + + + val stateEventsChunk = ChunkEntity.findWithNextToken(realm, roomId, DBConstants.STATE_EVENTS_CHUNK_TOKEN) + ?: ChunkEntity().apply { + this.prevToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN + this.nextToken = DBConstants.STATE_EVENTS_CHUNK_TOKEN + } + + chunkEvent.stateEvents.forEach { stateEvent -> + val eventEntity = stateEvent.asEntity().let { + realm.copyToRealmOrUpdate(it) + } + if (!stateEventsChunk.events.contains(eventEntity)) { + stateEventsChunk.events.add(0, eventEntity) + } + } + + chunkEvent.chunk.forEach { event -> val eventEntity = event.asEntity().let { realm.copyToRealmOrUpdate(it) } @@ -101,13 +118,14 @@ class PaginationRequest(private val roomAPI: RoomAPI, roomEntity.chunks.remove(prevChunk) } else if (hasOverlapped) { - chunksOverlapped.forEach { chunk -> - chunk.events.forEach { event -> + chunksOverlapped.forEach { overlapped -> + overlapped.events.forEach { event -> if (!currentChunk.events.contains(event)) { currentChunk.events.add(0, event) } } - roomEntity.chunks.remove(chunk) + currentChunk.prevToken = overlapped.prevToken + roomEntity.chunks.remove(overlapped) } } if (!roomEntity.chunks.contains(currentChunk)) { 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 5395609e..f62756d7 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 @@ -32,7 +32,7 @@ class TimelineBoundaryCallback(private val paginationRequest: PaginationRequest, return@doWithRealm } val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtEnd.eventId)).firstOrNull() - paginationRequest.execute(roomId, chunkEntity?.nextToken, PaginationDirection.FORWARDS, callback = createCallback(it)) + paginationRequest.execute(roomId, chunkEntity?.prevToken, PaginationDirection.BACKWARDS, callback = createCallback(it)) } } } @@ -44,7 +44,7 @@ class TimelineBoundaryCallback(private val paginationRequest: PaginationRequest, return@doWithRealm } val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtFront.eventId)).firstOrNull() - paginationRequest.execute(roomId, chunkEntity?.prevToken, PaginationDirection.BACKWARDS, callback = createCallback(it)) + paginationRequest.execute(roomId, chunkEntity?.nextToken, PaginationDirection.FORWARDS, callback = createCallback(it)) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt index 930aea43..1bdd7f1c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/sync/RoomSyncHandler.kt @@ -7,7 +7,7 @@ import im.vector.matrix.android.internal.database.mapper.asEntity import im.vector.matrix.android.internal.database.model.ChunkEntity import im.vector.matrix.android.internal.database.model.RoomEntity import im.vector.matrix.android.internal.database.query.findAllIncludingEvents -import im.vector.matrix.android.internal.database.query.findLastFromRoom +import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync import im.vector.matrix.android.internal.session.sync.model.RoomSync @@ -95,14 +95,13 @@ class RoomSyncHandler(private val monarchy: Monarchy) { isLimited: Boolean = true): ChunkEntity { val chunkEntity = if (!isLimited) { - ChunkEntity.findLastFromRoom(realm, roomId) + ChunkEntity.findLastLiveChunkFromRoom(realm, roomId) } else { val eventIds = eventList.filter { it.eventId != null }.map { it.eventId!! } ChunkEntity.findAllIncludingEvents(realm, eventIds).firstOrNull() } ?: ChunkEntity().apply { this.prevToken = prevToken } chunkEntity.nextToken = nextToken - chunkEntity.isLimited = isLimited eventList.forEach { event -> val eventEntity = event.asEntity().let {