Room update: start handling tombstone and room create events [WIP]

This commit is contained in:
ganfra
2019-07-25 19:34:39 +02:00
parent 6176520805
commit 9e5c70dda3
23 changed files with 362 additions and 18 deletions

View File

@ -34,5 +34,6 @@ data class RoomSummary(
val notificationCount: Int = 0,
val highlightCount: Int = 0,
val tags: List<RoomTag> = emptyList(),
val membership: Membership = Membership.NONE
val membership: Membership = Membership.NONE,
val isVersioned: Boolean = false
)

View File

@ -0,0 +1,42 @@
/*
*
* * Copyright 2019 New Vector Ltd
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package im.vector.matrix.android.api.session.room.model.create
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Content of a m.room.create type event
*/
@JsonClass(generateAdapter = true)
data class RoomCreateContent(
@Json(name = "creator") val creator: String,
@Json(name = "room_version") val roomVersion: String?,
@Json(name = "predecessor") val predecessor: Predecessor?
)
/**
* A link to an old room in case of room versioning
*/
@JsonClass(generateAdapter = true)
data class Predecessor(
@Json(name = "room_id") val roomId: String,
@Json(name = "event_id") val eventId: String
)

View File

@ -0,0 +1,31 @@
/*
*
* * Copyright 2019 New Vector Ltd
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package im.vector.matrix.android.api.session.room.model.tombstone
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
/**
* Class to contains Tombstone information
*/
@JsonClass(generateAdapter = true)
data class RoomTombstoneContent(
@Json(name = "body") val body: String? = null,
@Json(name = "replacement_room") val replacementRoom: String
)

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.api.session.room.state
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.Event
interface StateService {
@ -25,4 +26,6 @@ interface StateService {
*/
fun updateTopic(topic: String, callback: MatrixCallback<Unit>)
fun getStateEvent(eventType: String): Event?
}

View File

@ -61,7 +61,8 @@ internal class RoomSummaryMapper @Inject constructor(
highlightCount = roomSummaryEntity.highlightCount,
notificationCount = roomSummaryEntity.notificationCount,
tags = tags,
membership = roomSummaryEntity.membership
membership = roomSummaryEntity.membership,
isVersioned = roomSummaryEntity.isVersioned
)
}
}

View File

@ -35,7 +35,8 @@ internal open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
var otherMemberIds: RealmList<String> = RealmList(),
var notificationCount: Int = 0,
var highlightCount: Int = 0,
var tags: RealmList<RoomTagEntity> = RealmList()
var tags: RealmList<RoomTagEntity> = RealmList(),
var isVersioned: Boolean = false
) : RealmObject() {
private var membershipStr: String = Membership.NONE.name

View File

@ -37,6 +37,7 @@ import im.vector.matrix.android.internal.network.AccessTokenInterceptor
import im.vector.matrix.android.internal.network.RetrofitFactory
import im.vector.matrix.android.internal.session.group.GroupSummaryUpdater
import im.vector.matrix.android.internal.session.room.EventRelationsAggregationUpdater
import im.vector.matrix.android.internal.session.room.tombstone.RoomTombstoneEventLiveObserver
import im.vector.matrix.android.internal.session.room.prune.EventsPruner
import im.vector.matrix.android.internal.util.md5
import io.realm.RealmConfiguration
@ -128,6 +129,11 @@ internal abstract class SessionModule {
@IntoSet
abstract fun bindEventRelationsAggregationUpdater(groupSummaryUpdater: EventRelationsAggregationUpdater): LiveEntityObserver
@Binds
@IntoSet
abstract fun bindRoomCreateEventLiveObserver(roomTombstoneEventLiveObserver: RoomTombstoneEventLiveObserver): LiveEntityObserver
@Binds
abstract fun bindInitialSyncProgressService(initialSyncProgressService: DefaultInitialSyncProgressService): InitialSyncProgressService

View File

@ -54,7 +54,11 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
return monarchy.findAllMappedWithChanges(
{ realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
{ realm ->
RoomSummaryEntity.where(realm)
.isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
.notEqualTo(RoomSummaryEntityFields.IS_VERSIONED, true)
},
{ roomSummaryMapper.map(it) }
)
}

View File

@ -22,6 +22,7 @@ import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.CryptoService
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.internal.database.mapper.RoomSummaryMapper
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.session.room.membership.DefaultMembershipService
import im.vector.matrix.android.internal.session.room.membership.LoadRoomMembersTask
import im.vector.matrix.android.internal.session.room.membership.joining.InviteTask
@ -40,11 +41,14 @@ import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineSe
import im.vector.matrix.android.internal.session.room.timeline.GetContextOfEventTask
import im.vector.matrix.android.internal.session.room.timeline.PaginationTask
import im.vector.matrix.android.internal.task.TaskExecutor
import io.realm.RealmConfiguration
import javax.inject.Inject
internal class RoomFactory @Inject constructor(private val context: Context,
private val credentials: Credentials,
private val monarchy: Monarchy,
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val eventFactory: LocalEchoEventFactory,
private val roomSummaryMapper: RoomSummaryMapper,
private val taskExecutor: TaskExecutor,
@ -63,7 +67,7 @@ internal class RoomFactory @Inject constructor(private val context: Context,
fun create(roomId: String): Room {
val timelineService = DefaultTimelineService(roomId, monarchy, taskExecutor, contextOfEventTask, cryptoService, paginationTask)
val sendService = DefaultSendService(context, credentials, roomId, eventFactory, cryptoService, monarchy)
val stateService = DefaultStateService(roomId, taskExecutor, sendStateTask)
val stateService = DefaultStateService(roomId, realmConfiguration, taskExecutor, sendStateTask)
val roomMembersService = DefaultMembershipService(roomId, monarchy, taskExecutor, loadRoomMembersTask, inviteTask, joinRoomTask, leaveRoomTask)
val readService = DefaultReadService(roomId, monarchy, taskExecutor, setReadMarkersTask, credentials)
val relationService = DefaultRelationService(context,

View File

@ -17,16 +17,32 @@
package im.vector.matrix.android.internal.session.room.state
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.state.StateService
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.query.prev
import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.di.SessionDatabase
import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith
import io.realm.Realm
import io.realm.RealmConfiguration
import javax.inject.Inject
internal class DefaultStateService @Inject constructor(private val roomId: String,
@SessionDatabase
private val realmConfiguration: RealmConfiguration,
private val taskExecutor: TaskExecutor,
private val sendStateTask: SendStateTask) : StateService {
override fun getStateEvent(eventType: String): Event? {
return Realm.getInstance(realmConfiguration).use { realm ->
EventEntity.where(realm, roomId, eventType).prev()?.asDomain()
}
}
override fun updateTopic(topic: String, callback: MatrixCallback<Unit>) {
val params = SendStateTask.Params(roomId,
EventType.STATE_ROOM_TOPIC,
@ -39,4 +55,6 @@ internal class DefaultStateService @Inject constructor(private val roomId: Strin
.dispatchTo(callback)
.executeBy(taskExecutor)
}
}

View File

@ -0,0 +1,75 @@
/*
*
* * Copyright 2019 New Vector Ltd
* *
* * Licensed under the Apache License, Version 2.0 (the "License");
* * you may not use this file except in compliance with the License.
* * You may obtain a copy of the License at
* *
* * http://www.apache.org/licenses/LICENSE-2.0
* *
* * Unless required by applicable law or agreed to in writing, software
* * distributed under the License is distributed on an "AS IS" BASIS,
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* * See the License for the specific language governing permissions and
* * limitations under the License.
*
*/
package im.vector.matrix.android.internal.session.room.tombstone
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.tombstone.RoomTombstoneContent
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.RoomSummaryEntity
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 io.realm.OrderedCollectionChangeSet
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmResults
import javax.inject.Inject
internal class RoomTombstoneEventLiveObserver @Inject constructor(@SessionDatabase
realmConfiguration: RealmConfiguration)
: RealmLiveEntityObserver<EventEntity>(realmConfiguration) {
override val query = Monarchy.Query<EventEntity> {
EventEntity.types(it, listOf(EventType.STATE_ROOM_TOMBSTONE))
}
override fun onChange(results: RealmResults<EventEntity>, changeSet: OrderedCollectionChangeSet) {
changeSet.insertions
.asSequence()
.mapNotNull {
results[it]?.asDomain()
}
.toList()
.also {
handleRoomTombstoneEvents(it)
}
}
private fun handleRoomTombstoneEvents(tombstoneEvents: List<Event>) = Realm.getInstance(realmConfiguration).use {
it.executeTransactionAsync { realm ->
for (event in tombstoneEvents) {
if (event.roomId == null) continue
val createRoomContent = event.getClearContent().toModel<RoomTombstoneContent>()
if (createRoomContent?.replacementRoom == null) continue
val predecessorRoomSummary = RoomSummaryEntity.where(realm, event.roomId).findFirst()
?: RoomSummaryEntity(event.roomId)
predecessorRoomSummary.isVersioned = true
realm.insertOrUpdate(predecessorRoomSummary)
}
}
}
}

View File

@ -146,8 +146,7 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
roomEntity,
roomSync.timeline.events,
roomSync.timeline.prevToken,
roomSync.timeline.limited,
0
roomSync.timeline.limited
)
roomEntity.addOrUpdate(chunkEntity)
}
@ -195,15 +194,18 @@ internal class RoomSyncHandler @Inject constructor(private val monarchy: Monarch
roomEntity: RoomEntity,
eventList: List<Event>,
prevToken: String? = null,
isLimited: Boolean = true,
stateIndexOffset: Int = 0): ChunkEntity {
isLimited: Boolean = true): ChunkEntity {
val lastChunk = ChunkEntity.findLastLiveChunkFromRoom(realm, roomEntity.roomId)
var stateIndexOffset = 0
val chunkEntity = if (!isLimited && lastChunk != null) {
lastChunk
} else {
realm.createObject<ChunkEntity>().apply { this.prevToken = prevToken }
}
if (isLimited && lastChunk != null) {
stateIndexOffset = lastChunk.lastStateIndex(PaginationDirection.FORWARDS)
}
lastChunk?.isLastForward = false
chunkEntity.isLastForward = true

View File

@ -31,6 +31,7 @@
<string name="notice_room_visibility_world_readable">anyone.</string>
<string name="notice_room_visibility_unknown">unknown (%s).</string>
<string name="notice_end_to_end">%1$s turned on end-to-end encryption (%2$s)</string>
<string name="notice_room_update">%s upgraded this room.</string>
<string name="notice_requested_voip_conference">%1$s requested a VoIP conference</string>
<string name="notice_voip_started">VoIP conference started</string>