Merge pull request #334 from vector-im/feature/general_perf

Feature/general perf
This commit is contained in:
ganfra 2019-07-11 15:52:00 +02:00 committed by GitHub
commit 252b2ea30a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
25 changed files with 121 additions and 115 deletions

View file

@ -19,4 +19,4 @@ package im.vector.matrix.android
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main) internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, Main)

View file

@ -30,7 +30,7 @@ import im.vector.matrix.android.api.session.room.send.SendState
*/ */
data class TimelineEvent( data class TimelineEvent(
val root: Event, val root: Event,
val localId: String, val localId: Long,
val displayIndex: Int, val displayIndex: Int,
val senderName: String?, val senderName: String?,
val isUniqueDisplayName: Boolean, val isUniqueDisplayName: Boolean,

View file

@ -19,6 +19,7 @@ package im.vector.matrix.android.internal.database
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.internal.util.createBackgroundHandler import im.vector.matrix.android.internal.util.createBackgroundHandler
import io.realm.OrderedCollectionChangeSet import io.realm.OrderedCollectionChangeSet
import io.realm.OrderedRealmCollectionChangeListener
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmObject import io.realm.RealmObject
@ -33,7 +34,7 @@ internal interface LiveEntityObserver {
} }
internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val realmConfiguration: RealmConfiguration) internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val realmConfiguration: RealmConfiguration)
: LiveEntityObserver { : LiveEntityObserver, OrderedRealmCollectionChangeListener<RealmResults<T>> {
private companion object { private companion object {
val BACKGROUND_HANDLER = createBackgroundHandler("LIVE_ENTITY_BACKGROUND") val BACKGROUND_HANDLER = createBackgroundHandler("LIVE_ENTITY_BACKGROUND")
@ -50,9 +51,7 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
val realm = Realm.getInstance(realmConfiguration) val realm = Realm.getInstance(realmConfiguration)
backgroundRealm.set(realm) backgroundRealm.set(realm)
val queryResults = query.createQuery(realm).findAll() val queryResults = query.createQuery(realm).findAll()
queryResults.addChangeListener { t, changeSet -> queryResults.addChangeListener(this)
onChanged(t, changeSet)
}
results = AtomicReference(queryResults) results = AtomicReference(queryResults)
} }
} }
@ -73,19 +72,4 @@ internal abstract class RealmLiveEntityObserver<T : RealmObject>(protected val r
return isStarted.get() return isStarted.get()
} }
private fun onChanged(realmResults: RealmResults<T>, changeSet: OrderedCollectionChangeSet) {
val insertionIndexes = changeSet.insertions
val updateIndexes = changeSet.changes
val deletionIndexes = changeSet.deletions
val inserted = realmResults.filterIndexed { index, _ -> insertionIndexes.contains(index) }
val updated = realmResults.filterIndexed { index, _ -> updateIndexes.contains(index) }
val deleted = realmResults.filterIndexed { index, _ -> deletionIndexes.contains(index) }
processChanges(inserted, updated, deleted)
}
/**
* Do quick treatment or delegate on a task
*/
protected abstract fun processChanges(inserted: List<T>, updated: List<T>, deleted: List<T>)
} }

View file

@ -134,7 +134,8 @@ internal fun ChunkEntity.add(roomId: String,
} }
} }
val eventEntity = TimelineEventEntity().also { val localId = TimelineEventEntity.nextId(realm)
val eventEntity = TimelineEventEntity(localId).also {
it.root = event.toEntity(roomId).apply { it.root = event.toEntity(roomId).apply {
this.stateIndex = currentStateIndex this.stateIndex = currentStateIndex
this.isUnlinked = isUnlinked this.isUnlinked = isUnlinked

View file

@ -26,7 +26,6 @@ import im.vector.matrix.android.internal.database.query.fastContains
import im.vector.matrix.android.internal.extensions.assertIsManaged import im.vector.matrix.android.internal.extensions.assertIsManaged
import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.room.membership.RoomMembers
internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) { internal fun RoomEntity.deleteOnCascade(chunkEntity: ChunkEntity) {
chunks.remove(chunkEntity) chunks.remove(chunkEntity)
chunkEntity.deleteOnCascade() chunkEntity.deleteOnCascade()
@ -65,7 +64,8 @@ internal fun RoomEntity.addSendingEvent(event: Event) {
} }
val roomMembers = RoomMembers(realm, roomId) val roomMembers = RoomMembers(realm, roomId)
val myUser = roomMembers.get(senderId) val myUser = roomMembers.get(senderId)
val timelineEventEntity = TimelineEventEntity().also { val localId = TimelineEventEntity.nextId(realm)
val timelineEventEntity = TimelineEventEntity(localId).also {
it.root = eventEntity it.root = eventEntity
it.eventId = event.eventId ?: "" it.eventId = event.eventId ?: ""
it.roomId = roomId it.roomId = roomId

View file

@ -30,9 +30,11 @@ import im.vector.matrix.android.internal.database.query.prev
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.extensions.assertIsManaged import im.vector.matrix.android.internal.extensions.assertIsManaged
import im.vector.matrix.android.internal.session.room.membership.RoomMembers import im.vector.matrix.android.internal.session.room.membership.RoomMembers
import io.realm.Realm
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmQuery import io.realm.RealmQuery
internal fun TimelineEventEntity.updateSenderData() { internal fun TimelineEventEntity.updateSenderData() {
assertIsManaged() assertIsManaged()
val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return val roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst() ?: return
@ -69,6 +71,15 @@ internal fun TimelineEventEntity.updateSenderData() {
this.senderMembershipEvent = senderMembershipEvent this.senderMembershipEvent = senderMembershipEvent
} }
internal fun TimelineEventEntity.Companion.nextId(realm: Realm): Long{
val currentIdNum = realm.where(TimelineEventEntity::class.java).max(TimelineEventEntityFields.LOCAL_ID)
return if (currentIdNum == null) {
1
} else {
currentIdNum.toLong() + 1
}
}
private fun RealmList<TimelineEventEntity>.buildQuery(sender: String, isUnlinked: Boolean): RealmQuery<TimelineEventEntity> { private fun RealmList<TimelineEventEntity>.buildQuery(sender: String, isUnlinked: Boolean): RealmQuery<TimelineEventEntity> {
return where() return where()
.equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, sender) .equalTo(TimelineEventEntityFields.ROOT.STATE_KEY, sender)

View file

@ -35,7 +35,7 @@ internal object EventMapper {
val uds = if (event.unsignedData == null) null val uds = if (event.unsignedData == null) null
else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(event.unsignedData) else MoshiProvider.providesMoshi().adapter(UnsignedData::class.java).toJson(event.unsignedData)
val eventEntity = EventEntity() val eventEntity = EventEntity()
eventEntity.eventId = event.eventId ?: UUID.randomUUID().toString() eventEntity.eventId = event.eventId ?: ""
eventEntity.roomId = event.roomId ?: roomId eventEntity.roomId = event.roomId ?: roomId
eventEntity.content = ContentMapper.map(event.content) eventEntity.content = ContentMapper.map(event.content)
val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent val resolvedPrevContent = event.prevContent ?: event.unsignedData?.prevContent

View file

@ -23,15 +23,6 @@ import im.vector.matrix.android.internal.database.model.TimelineEventEntity
internal object TimelineEventMapper { internal object TimelineEventMapper {
fun map(timelineEvent: TimelineEvent, roomId: String): TimelineEventEntity {
val timelineEventEntity = TimelineEventEntity()
timelineEventEntity.root = timelineEvent.root.toEntity(roomId)
timelineEventEntity.eventId = timelineEvent.root.eventId ?: ""
timelineEventEntity.roomId = roomId
timelineEventEntity.annotations = timelineEvent.annotations?.let { EventAnnotationsSummaryMapper.map(it, roomId) }
return timelineEventEntity
}
fun map(timelineEventEntity: TimelineEventEntity): TimelineEvent { fun map(timelineEventEntity: TimelineEventEntity): TimelineEvent {
return TimelineEvent( return TimelineEvent(
@ -53,8 +44,4 @@ internal fun TimelineEventEntity.asDomain(): TimelineEvent {
return TimelineEventMapper.map(this) return TimelineEventMapper.map(this)
} }
internal fun TimelineEvent.toEntity(roomId: String): TimelineEventEntity {
return TimelineEventMapper.map(this, roomId)
}

View file

@ -27,8 +27,7 @@ import io.realm.annotations.LinkingObjects
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
import java.util.* import java.util.*
internal open class EventEntity(@PrimaryKey var localId: String = UUID.randomUUID().toString(), internal open class EventEntity(@Index var eventId: String = "",
@Index var eventId: String = "",
@Index var roomId: String = "", @Index var roomId: String = "",
@Index var type: String = "", @Index var type: String = "",
var content: String? = null, var content: String? = null,

View file

@ -16,10 +16,6 @@
package im.vector.matrix.android.internal.database.model package im.vector.matrix.android.internal.database.model
import com.squareup.moshi.Types
import im.vector.matrix.android.internal.crypto.MXEventDecryptionResult
import im.vector.matrix.android.internal.database.mapper.ContentMapper
import im.vector.matrix.android.internal.di.MoshiProvider
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.RealmResults import io.realm.RealmResults
import io.realm.annotations.Index import io.realm.annotations.Index
@ -28,7 +24,7 @@ import io.realm.annotations.PrimaryKey
import java.util.* import java.util.*
internal open class TimelineEventEntity(@PrimaryKey var localId: String = UUID.randomUUID().toString(), internal open class TimelineEventEntity(var localId: Long = 0,
@Index var eventId: String = "", @Index var eventId: String = "",
@Index var roomId: String = "", @Index var roomId: String = "",
var root: EventEntity? = null, var root: EventEntity? = null,

View file

@ -18,15 +18,15 @@ package im.vector.matrix.android.internal.di
import android.content.Context import android.content.Context
import android.content.res.Resources import android.content.res.Resources
import android.os.Handler
import android.os.HandlerThread
import dagger.Module import dagger.Module
import dagger.Provides import dagger.Provides
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import im.vector.matrix.android.internal.util.createBackgroundHandler import im.vector.matrix.android.internal.util.createBackgroundHandler
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.android.asCoroutineDispatcher import kotlinx.coroutines.android.asCoroutineDispatcher
import kotlinx.coroutines.asCoroutineDispatcher
import org.matrix.olm.OlmManager import org.matrix.olm.OlmManager
import java.util.concurrent.Executors
@Module @Module
internal object MatrixModule { internal object MatrixModule {
@ -38,7 +38,8 @@ internal object MatrixModule {
return MatrixCoroutineDispatchers(io = Dispatchers.IO, return MatrixCoroutineDispatchers(io = Dispatchers.IO,
computation = Dispatchers.IO, computation = Dispatchers.IO,
main = Dispatchers.Main, main = Dispatchers.Main,
crypto = createBackgroundHandler("Crypto_Thread").asCoroutineDispatcher() crypto = createBackgroundHandler("Crypto_Thread").asCoroutineDispatcher(),
sync = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
) )
} }

View file

@ -20,6 +20,7 @@ import android.content.Context
import android.os.Looper import android.os.Looper
import androidx.annotation.MainThread import androidx.annotation.MainThread
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import dagger.Lazy
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.SessionParams import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.pushrules.PushRuleService import im.vector.matrix.android.api.pushrules.PushRuleService
@ -52,34 +53,34 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
private val context: Context, private val context: Context,
private val liveEntityObservers: Set<@JvmSuppressWildcards LiveEntityObserver>, private val liveEntityObservers: Set<@JvmSuppressWildcards LiveEntityObserver>,
private val sessionListeners: SessionListeners, private val sessionListeners: SessionListeners,
private val roomService: RoomService, private val roomService: Lazy<RoomService>,
private val roomDirectoryService: RoomDirectoryService, private val roomDirectoryService: Lazy<RoomDirectoryService>,
private val groupService: GroupService, private val groupService: Lazy<GroupService>,
private val userService: UserService, private val userService: Lazy<UserService>,
private val filterService: FilterService, private val filterService: Lazy<FilterService>,
private val cacheService: CacheService, private val cacheService: Lazy<CacheService>,
private val signOutService: SignOutService, private val signOutService: Lazy<SignOutService>,
private val pushRuleService: PushRuleService, private val pushRuleService: Lazy<PushRuleService>,
private val pushersService: PushersService, private val pushersService: Lazy<PushersService>,
private val cryptoService: CryptoManager, private val cryptoService: Lazy<CryptoManager>,
private val fileService: FileService, private val fileService: Lazy<FileService>,
private val syncThread: SyncThread, private val syncThread: SyncThread,
private val contentUrlResolver: ContentUrlResolver, private val contentUrlResolver: ContentUrlResolver,
private val contentUploadProgressTracker: ContentUploadStateTracker, private val contentUploadProgressTracker: ContentUploadStateTracker,
private val initialSyncProgressService: InitialSyncProgressService) private val initialSyncProgressService: Lazy<InitialSyncProgressService>)
: Session, : Session,
RoomService by roomService, RoomService by roomService.get(),
RoomDirectoryService by roomDirectoryService, RoomDirectoryService by roomDirectoryService.get(),
GroupService by groupService, GroupService by groupService.get(),
UserService by userService, UserService by userService.get(),
CryptoService by cryptoService, CryptoService by cryptoService.get(),
CacheService by cacheService, CacheService by cacheService.get(),
SignOutService by signOutService, SignOutService by signOutService.get(),
FilterService by filterService, FilterService by filterService.get(),
PushRuleService by pushRuleService, PushRuleService by pushRuleService.get(),
PushersService by pushersService, PushersService by pushersService.get(),
FileService by fileService, FileService by fileService.get(),
InitialSyncProgressService by initialSyncProgressService { InitialSyncProgressService by initialSyncProgressService.get() {
private var isOpen = false private var isOpen = false
@ -123,7 +124,7 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
assert(isOpen) assert(isOpen)
stopSync() stopSync()
liveEntityObservers.forEach { it.dispose() } liveEntityObservers.forEach { it.dispose() }
cryptoService.close() cryptoService.get().close()
isOpen = false isOpen = false
} }
@ -140,15 +141,15 @@ internal class DefaultSession @Inject constructor(override val sessionParams: Se
//syncThread.kill() //syncThread.kill()
Timber.w("SIGN_OUT: call webservice") Timber.w("SIGN_OUT: call webservice")
return signOutService.signOut(object : MatrixCallback<Unit> { return signOutService.get().signOut(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
Timber.w("SIGN_OUT: call webservice -> SUCCESS: clear cache") Timber.w("SIGN_OUT: call webservice -> SUCCESS: clear cache")
// Clear the cache // Clear the cache
cacheService.clearCache(object : MatrixCallback<Unit> { cacheService.get().clearCache(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
Timber.w("SIGN_OUT: clear cache -> SUCCESS: clear crypto cache") Timber.w("SIGN_OUT: clear cache -> SUCCESS: clear crypto cache")
cryptoService.clearCryptoCache(MatrixCallbackDelegate(callback)) cryptoService.get().clearCryptoCache(MatrixCallbackDelegate(callback))
WorkManagerUtil.cancelAllWorks(context) WorkManagerUtil.cancelAllWorks(context)
} }

View file

@ -22,13 +22,20 @@ import androidx.work.WorkManager
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.internal.database.RealmLiveEntityObserver import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.GroupEntity import im.vector.matrix.android.internal.database.model.GroupEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.room.prune.PruneEventTask
import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.worker.WorkManagerUtil import im.vector.matrix.android.internal.worker.WorkManagerUtil
import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder import im.vector.matrix.android.internal.worker.WorkManagerUtil.matrixOneTimeWorkRequestBuilder
import im.vector.matrix.android.internal.worker.WorkerParamsFactory import im.vector.matrix.android.internal.worker.WorkerParamsFactory
import io.realm.OrderedCollectionChangeSet
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmResults
import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER" private const val GET_GROUP_DATA_WORKER = "GET_GROUP_DATA_WORKER"
@ -40,8 +47,12 @@ internal class GroupSummaryUpdater @Inject constructor(private val context: Cont
override val query = Monarchy.Query<GroupEntity> { GroupEntity.where(it) } override val query = Monarchy.Query<GroupEntity> { GroupEntity.where(it) }
override fun processChanges(inserted: List<GroupEntity>, updated: List<GroupEntity>, deleted: List<GroupEntity>) { override fun onChange(results: RealmResults<GroupEntity>, changeSet: OrderedCollectionChangeSet) {
val newGroupIds = inserted.map { it.groupId } val newGroupIds = changeSet.insertions
.asSequence()
.mapNotNull { results[it]?.groupId}
.toList()
val getGroupDataWorkerParams = GetGroupDataWorker.Params(credentials.userId, newGroupIds) val getGroupDataWorkerParams = GetGroupDataWorker.Params(credentials.userId, newGroupIds)
val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams) val workData = WorkerParamsFactory.toData(getGroupDataWorkerParams)
@ -55,5 +66,4 @@ internal class GroupSummaryUpdater @Inject constructor(private val context: Cont
.enqueue() .enqueue()
} }
} }

View file

@ -25,7 +25,9 @@ import im.vector.matrix.android.internal.database.query.types
import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import io.realm.OrderedCollectionChangeSet
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmResults
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -50,19 +52,18 @@ internal class EventRelationsAggregationUpdater @Inject constructor(@SessionData
) )
} }
override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) { override fun onChange(results: RealmResults<EventEntity>, changeSet: OrderedCollectionChangeSet) {
Timber.v("EventRelationsAggregationUpdater called with ${inserted.size} insertions") Timber.v("EventRelationsAggregationUpdater called with ${changeSet.insertions.size} insertions")
val domainInserted = inserted
.map { it.asDomain() }
val insertedDomains = changeSet.insertions
.asSequence()
.mapNotNull { results[it]?.asDomain() }
.toList()
val params = EventRelationsAggregationTask.Params( val params = EventRelationsAggregationTask.Params(
domainInserted, insertedDomains,
credentials.userId credentials.userId
) )
task.configureWith(params).executeBy(taskExecutor)
task.configureWith(params)
.executeBy(taskExecutor)
} }
} }

View file

@ -26,9 +26,12 @@ import im.vector.matrix.android.internal.database.query.types
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationTask
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import io.realm.OrderedCollectionChangeSet
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmResults
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -44,18 +47,19 @@ internal class EventsPruner @Inject constructor(@SessionDatabase realmConfigurat
override val query = Monarchy.Query<EventEntity> { EventEntity.types(it, listOf(EventType.REDACTION)) } override val query = Monarchy.Query<EventEntity> { EventEntity.types(it, listOf(EventType.REDACTION)) }
override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) { override fun onChange(results: RealmResults<EventEntity>, changeSet: OrderedCollectionChangeSet) {
Timber.v("Event pruner called with ${inserted.size} insertions") Timber.v("Event pruner called with ${changeSet.insertions.size} insertions")
val redactionEvents = inserted.map { it.asDomain() }
val insertedDomains = changeSet.insertions
.asSequence()
.mapNotNull { results[it]?.asDomain() }
.toList()
val params = PruneEventTask.Params( val params = PruneEventTask.Params(
redactionEvents, insertedDomains,
credentials.userId credentials.userId
) )
pruneEventTask.configureWith(params).executeBy(taskExecutor)
pruneEventTask.configureWith(params)
.executeBy(taskExecutor)
} }
} }

View file

@ -43,7 +43,7 @@ import kotlin.collections.ArrayList
import kotlin.collections.HashMap import kotlin.collections.HashMap
private const val INITIAL_LOAD_SIZE = 20 private const val INITIAL_LOAD_SIZE = 10
private const val MIN_FETCHING_COUNT = 30 private const val MIN_FETCHING_COUNT = 30
private const val DISPLAY_INDEX_UNKNOWN = Int.MIN_VALUE private const val DISPLAY_INDEX_UNKNOWN = Int.MIN_VALUE
@ -216,6 +216,7 @@ internal class DefaultTimeline(
backgroundRealm.set(realm) backgroundRealm.set(realm)
clearUnlinkedEvents(realm) clearUnlinkedEvents(realm)
roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst()?.also { roomEntity = RoomEntity.where(realm, roomId = roomId).findFirst()?.also {
it.sendingTimelineEvents.addChangeListener { _ -> it.sendingTimelineEvents.addChangeListener { _ ->
postSnapshot() postSnapshot()
@ -242,6 +243,8 @@ internal class DefaultTimeline(
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId") Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
BACKGROUND_HANDLER.post { BACKGROUND_HANDLER.post {
cancelableBag.cancel() cancelableBag.cancel()
roomEntity?.sendingTimelineEvents?.removeAllChangeListeners()
eventRelations.removeAllChangeListeners()
liveEvents.removeAllChangeListeners() liveEvents.removeAllChangeListeners()
backgroundRealm.getAndSet(null).also { backgroundRealm.getAndSet(null).also {
it.close() it.close()

View file

@ -105,8 +105,8 @@ open class SyncService : Service() {
Timber.v("Execute sync request with timeout 0") Timber.v("Execute sync request with timeout 0")
val params = SyncTask.Params(TIME_OUT) val params = SyncTask.Params(TIME_OUT)
cancelableTask = syncTask.configureWith(params) cancelableTask = syncTask.configureWith(params)
.callbackOn(TaskThread.CALLER) .callbackOn(TaskThread.SYNC)
.executeOn(TaskThread.CALLER) .executeOn(TaskThread.SYNC)
.dispatchTo(object : MatrixCallback<Unit> { .dispatchTo(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
cancelableTask = null cancelableTask = null

View file

@ -97,8 +97,8 @@ internal class SyncThread @Inject constructor(private val syncTask: SyncTask,
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
val params = SyncTask.Params(DEFAULT_LONG_POOL_TIMEOUT) val params = SyncTask.Params(DEFAULT_LONG_POOL_TIMEOUT)
cancelableTask = syncTask.configureWith(params) cancelableTask = syncTask.configureWith(params)
.callbackOn(TaskThread.CALLER) .callbackOn(TaskThread.SYNC)
.executeOn(TaskThread.CALLER) .executeOn(TaskThread.SYNC)
.dispatchTo(object : MatrixCallback<Unit> { .dispatchTo(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
latch.countDown() latch.countDown()

View file

@ -62,8 +62,8 @@ internal class SyncWorker(context: Context,
val latch = CountDownLatch(1) val latch = CountDownLatch(1)
val taskParams = SyncTask.Params(0) val taskParams = SyncTask.Params(0)
cancelableTask = syncTask.configureWith(taskParams) cancelableTask = syncTask.configureWith(taskParams)
.callbackOn(TaskThread.CALLER) .callbackOn(TaskThread.SYNC)
.executeOn(TaskThread.CALLER) .executeOn(TaskThread.SYNC)
.dispatchTo(object : MatrixCallback<Unit> { .dispatchTo(object : MatrixCallback<Unit> {
override fun onSuccess(data: Unit) { override fun onSuccess(data: Unit) {
latch.countDown() latch.countDown()

View file

@ -22,13 +22,13 @@ import im.vector.matrix.android.internal.database.RealmLiveEntityObserver
import im.vector.matrix.android.internal.database.model.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.query.types import im.vector.matrix.android.internal.database.query.types
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.SessionScope
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import io.realm.OrderedCollectionChangeSet
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import io.realm.RealmResults
import io.realm.Sort import io.realm.Sort
import javax.inject.Inject import javax.inject.Inject
@ -42,15 +42,19 @@ internal class UserEntityUpdater @Inject constructor(@SessionDatabase realmConfi
.types(it, listOf(EventType.STATE_ROOM_MEMBER)) .types(it, listOf(EventType.STATE_ROOM_MEMBER))
.sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING) .sort(EventEntityFields.STATE_INDEX, Sort.DESCENDING)
.distinct(EventEntityFields.STATE_KEY) .distinct(EventEntityFields.STATE_KEY)
} }
override fun processChanges(inserted: List<EventEntity>, updated: List<EventEntity>, deleted: List<EventEntity>) { override fun onChange(results: RealmResults<EventEntity>, changeSet: OrderedCollectionChangeSet) {
val roomMembersEvents = inserted.map { it.eventId } val roomMembersEvents = changeSet.insertions
.asSequence()
.mapNotNull { results[it]?.eventId }
.toList()
val taskParams = UpdateUserTask.Params(roomMembersEvents) val taskParams = UpdateUserTask.Params(roomMembersEvents)
updateUserTask updateUserTask
.configureWith(taskParams) .configureWith(taskParams)
.executeOn(TaskThread.IO) .executeOn(TaskThread.IO)
.executeBy(taskExecutor) .executeBy(taskExecutor)
} }
} }

View file

@ -77,6 +77,7 @@ internal class TaskExecutor @Inject constructor(private val coroutineDispatchers
TaskThread.IO -> coroutineDispatchers.io TaskThread.IO -> coroutineDispatchers.io
TaskThread.CALLER -> EmptyCoroutineContext TaskThread.CALLER -> EmptyCoroutineContext
TaskThread.CRYPTO -> coroutineDispatchers.crypto TaskThread.CRYPTO -> coroutineDispatchers.crypto
TaskThread.SYNC -> coroutineDispatchers.sync
} }

View file

@ -21,5 +21,6 @@ internal enum class TaskThread {
COMPUTATION, COMPUTATION,
IO, IO,
CALLER, CALLER,
CRYPTO CRYPTO,
SYNC
} }

View file

@ -22,5 +22,6 @@ internal data class MatrixCoroutineDispatchers(
val io: CoroutineDispatcher, val io: CoroutineDispatcher,
val computation: CoroutineDispatcher, val computation: CoroutineDispatcher,
val main: CoroutineDispatcher, val main: CoroutineDispatcher,
val crypto: CoroutineDispatcher val crypto: CoroutineDispatcher,
val sync: CoroutineDispatcher
) )

View file

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.LongSparseArray
import android.view.View import android.view.View
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListUpdateCallback import androidx.recyclerview.widget.ListUpdateCallback
@ -82,8 +83,8 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
fun onUrlLongClicked(url: String): Boolean fun onUrlLongClicked(url: String): Boolean
} }
private val collapsedEventIds = linkedSetOf<String>() private val collapsedEventIds = linkedSetOf<Long>()
private val mergeItemCollapseStates = HashMap<String, Boolean>() private val mergeItemCollapseStates = HashMap<Long,Boolean>()
private val modelCache = arrayListOf<CacheItemData?>() private val modelCache = arrayListOf<CacheItemData?>()
private var currentSnapshot: List<TimelineEvent> = emptyList() private var currentSnapshot: List<TimelineEvent> = emptyList()
@ -298,7 +299,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
} else { } else {
collapsedEventIds.removeAll(mergedEventIds) collapsedEventIds.removeAll(mergedEventIds)
} }
val mergeId = mergedEventIds.joinToString(separator = "_") { it } val mergeId = mergedEventIds.joinToString(separator = "_") { it.toString() }
MergedHeaderItem(isCollapsed, mergeId, mergedData, avatarRenderer) { MergedHeaderItem(isCollapsed, mergeId, mergedData, avatarRenderer) {
mergeItemCollapseStates[event.localId] = it mergeItemCollapseStates[event.localId] = it
requestModelBuild() requestModelBuild()
@ -329,7 +330,7 @@ class TimelineEventController @Inject constructor(private val dateFormatter: Tim
} }
private data class CacheItemData( private data class CacheItemData(
val localId: String, val localId: Long,
val eventId: String?, val eventId: String?,
val eventModel: EpoxyModel<*>? = null, val eventModel: EpoxyModel<*>? = null,
val mergedHeaderModel: MergedHeaderItem? = null, val mergedHeaderModel: MergedHeaderItem? = null,

View file

@ -78,7 +78,7 @@ data class MergedHeaderItem(private val isCollapsed: Boolean,
} }
data class Data( data class Data(
val eventId: String, val eventId: Long,
val userId: String, val userId: String,
val memberName: String, val memberName: String,
val avatarUrl: String? val avatarUrl: String?