From 82180304ef2a7a735f79b88134285a05517dcdb8 Mon Sep 17 00:00:00 2001 From: ganfra Date: Thu, 21 Jul 2022 17:07:33 +0200 Subject: [PATCH] Realm: add PagedList implementation --- .../pagedlist/RealmTiledDataSource.kt | 67 +++++++++++++++++++ .../database/pagedlist/TiledDataSource.kt | 42 ++++++++++++ 2 files changed, 109 insertions(+) create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/RealmTiledDataSource.kt create mode 100644 matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/TiledDataSource.kt diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/RealmTiledDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/RealmTiledDataSource.kt new file mode 100644 index 0000000000..53907d8b85 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/RealmTiledDataSource.kt @@ -0,0 +1,67 @@ +package org.matrix.android.sdk.internal.database.pagedlist + +import androidx.paging.DataSource +import io.realm.kotlin.Realm +import io.realm.kotlin.notifications.UpdatedResults +import io.realm.kotlin.query.RealmQuery +import io.realm.kotlin.query.RealmResults +import io.realm.kotlin.types.RealmObject +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.cancelChildren +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +internal class RealmTiledDataSource internal constructor( + realm: Realm, + queryBuilder: (Realm) -> RealmQuery, + coroutineScope: CoroutineScope +) : + TiledDataSource() { + + class Factory( + private val realm: Realm, + private val queryBuilder: (Realm) -> RealmQuery, + private val coroutineScope: CoroutineScope, + ) : DataSource.Factory() { + + override fun create(): DataSource { + val childScope = CoroutineScope(SupervisorJob() + coroutineScope.coroutineContext) + return RealmTiledDataSource(realm, queryBuilder, childScope) + } + } + + private val results: RealmResults + + init { + addInvalidatedCallback { + coroutineScope.coroutineContext.cancelChildren() + } + results = queryBuilder(realm).find() + results.asFlow() + .onEach { resultsChange -> + when (resultsChange) { + is UpdatedResults -> invalidate() + else -> Unit + } + } + .launchIn(coroutineScope) + } + + override fun countItems(): Int { + return results.size + } + + override fun loadRange(startPosition: Int, count: Int): List { + val size = countItems() + if (size == 0) return emptyList() + return buildList { + val endPosition = minOf(startPosition + count, size) + for (position in startPosition until endPosition) { + results.getOrNull(position)?.also { item -> + add(item) + } + } + } + } +} diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/TiledDataSource.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/TiledDataSource.kt new file mode 100644 index 0000000000..d9b40d6542 --- /dev/null +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/internal/database/pagedlist/TiledDataSource.kt @@ -0,0 +1,42 @@ +package org.matrix.android.sdk.internal.database.pagedlist + +import android.annotation.SuppressLint +import androidx.paging.PositionalDataSource + +@Suppress("DEPRECATION") +public abstract class TiledDataSource : PositionalDataSource() { + + public abstract fun countItems(): Int + + public abstract fun loadRange(startPosition: Int, count: Int): List? + + @SuppressLint("RestrictedApi") // For computeInitialLoadPosition, computeInitialLoadSize + override fun loadInitial( + params: LoadInitialParams, + callback: LoadInitialCallback + ) { + val totalCount = countItems() + if (totalCount == 0) { + callback.onResult(emptyList(), 0, 0) + return + } + val firstLoadPosition = computeInitialLoadPosition(params, totalCount) + val firstLoadSize = computeInitialLoadSize(params, firstLoadPosition, totalCount) + + val list = loadRange(firstLoadPosition, firstLoadSize) + if (list != null && list.size == firstLoadSize) { + callback.onResult(list, firstLoadPosition, totalCount) + } else { + invalidate() + } + } + + override fun loadRange(params: LoadRangeParams, callback: LoadRangeCallback) { + val list = loadRange(params.startPosition, params.loadSize) + if (list != null) { + callback.onResult(list) + } else { + invalidate() + } + } +}