Sync : start insertion in DB

This commit is contained in:
ganfra 2018-10-12 19:26:22 +02:00
parent 9bef41a13b
commit 1a967b6326
11 changed files with 369 additions and 176 deletions

View File

@ -54,11 +54,93 @@
} }
], ],
"relations": [] "relations": []
},
{
"id": "8:1761107158737061066",
"lastPropertyId": "3:787896491046945850",
"name": "RoomEntity",
"properties": [
{
"id": "1:7021996505563941556",
"name": "id"
},
{
"id": "2:1269026517652311997",
"name": "membership"
},
{
"id": "3:787896491046945850",
"name": "roomId"
}
],
"relations": [
{
"id": "1:5345919940124619772",
"name": "chunks"
}
]
},
{
"id": "9:6019404356061358330",
"lastPropertyId": "3:3869954049926012588",
"name": "ChunkEntity",
"properties": [
{
"id": "1:3461196015528391821",
"name": "id"
},
{
"id": "2:4852790652764050720",
"name": "prevToken"
},
{
"id": "3:3869954049926012588",
"name": "nextToken"
}
],
"relations": [
{
"id": "2:5944867505344991328",
"name": "events"
}
]
},
{
"id": "10:7579750767964019860",
"lastPropertyId": "6:6069780711726222192",
"name": "EventEntity",
"properties": [
{
"id": "1:3170318841756889053",
"name": "id"
},
{
"id": "2:6651269605038929561",
"name": "type"
},
{
"id": "3:470436998712759552",
"name": "content"
},
{
"id": "4:1820638541606769650",
"name": "eventId"
},
{
"id": "5:4423270757987421556",
"name": "prevContent"
},
{
"id": "6:6069780711726222192",
"name": "stateKey"
}
],
"relations": []
} }
], ],
"lastEntityId": "7:6920598293865885979", "lastEntityId": "10:7579750767964019860",
"lastIndexId": "4:1616715309690181473", "lastIndexId": "4:1616715309690181473",
"lastRelationId": "0:0", "lastRelationId": "2:5944867505344991328",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",
"modelVersion": 4, "modelVersion": 4,
"modelVersionParserMinimum": 4, "modelVersionParserMinimum": 4,

View File

@ -3,27 +3,6 @@
"_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.", "_note2": "ObjectBox manages crucial IDs for your object model. See docs for details.",
"_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.", "_note3": "If you have VCS merge conflicts, you must resolve them according to ObjectBox docs.",
"entities": [ "entities": [
{
"id": "4:1775102368193732759",
"lastPropertyId": "6:8817556524159529201",
"name": "SessionParams",
"properties": [
{
"id": "2:5690571528511905880",
"name": "homeServerConnectionConfig"
},
{
"id": "3:1811444397594387116",
"name": "id"
},
{
"id": "6:8817556524159529201",
"indexId": "4:1616715309690181473",
"name": "credentialsRelationId"
}
],
"relations": []
},
{ {
"id": "6:5991986256653472999", "id": "6:5991986256653472999",
"lastPropertyId": "6:5675340775405504775", "lastPropertyId": "6:5675340775405504775",
@ -55,9 +34,29 @@
} }
], ],
"relations": [] "relations": []
},
{
"id": "7:6920598293865885979",
"lastPropertyId": "3:5795529890406591571",
"name": "ObjectBoxSessionParams",
"properties": [
{
"id": "1:7073183456619398402",
"name": "credentialsJson"
},
{
"id": "2:8905537494398356078",
"name": "homeServerConnectionConfigJson"
},
{
"id": "3:5795529890406591571",
"name": "id"
}
],
"relations": []
} }
], ],
"lastEntityId": "6:5991986256653472999", "lastEntityId": "7:6920598293865885979",
"lastIndexId": "4:1616715309690181473", "lastIndexId": "4:1616715309690181473",
"lastRelationId": "0:0", "lastRelationId": "0:0",
"lastSequenceId": "0:0", "lastSequenceId": "0:0",
@ -67,12 +66,14 @@
637433444018824383, 637433444018824383,
5577202525246066978, 5577202525246066978,
213989653016909134, 213989653016909134,
826321009570992494 826321009570992494,
1775102368193732759
], ],
"retiredIndexUids": [ "retiredIndexUids": [
7573100044105293219, 7573100044105293219,
7664899561635023422, 7664899561635023422,
762622607983996029 762622607983996029,
1616715309690181473
], ],
"retiredPropertyUids": [ "retiredPropertyUids": [
6211403495341530846, 6211403495341530846,
@ -101,7 +102,10 @@
8488502568193639300, 8488502568193639300,
594512591943458255, 594512591943458255,
5113574037182501000, 5113574037182501000,
4929796643260285955 4929796643260285955,
5690571528511905880,
1811444397594387116,
8817556524159529201
], ],
"retiredRelationUids": [], "retiredRelationUids": [],
"version": 1 "version": 1

View File

@ -0,0 +1,25 @@
package im.vector.matrix.android.internal.database.mapper
import com.squareup.moshi.Types
import im.vector.matrix.android.api.events.Event
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.di.MoshiProvider
object EventMapper {
private val moshi = MoshiProvider.providesMoshi()
private val type = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)
private val adapter = moshi.adapter<Map<String, Any>>(type)
fun map(event: Event): EventEntity {
val eventEntity = EventEntity()
eventEntity.eventId = event.eventId
eventEntity.content = adapter.toJson(event.content)
eventEntity.prevContent = adapter.toJson(event.prevContent)
eventEntity.stateKey = event.stateKey
eventEntity.type = event.type
return eventEntity
}
}

View File

@ -6,9 +6,9 @@ import io.objectbox.annotation.Id
@Entity @Entity
class EventEntity { class EventEntity {
@Id var id: Long = 0 @Id var id: Long = 0
lateinit var eventId: String
lateinit var type: String lateinit var type: String
lateinit var content: String lateinit var content: String
var eventId: String? = null
var prevContent: String? = null var prevContent: String? = null
var stateKey: String? = null var stateKey: String? = null
} }

View File

@ -1,12 +1,37 @@
package im.vector.matrix.android.internal.database.model package im.vector.matrix.android.internal.database.model
import io.objectbox.annotation.Convert
import io.objectbox.annotation.Entity import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id import io.objectbox.annotation.Id
import io.objectbox.converter.PropertyConverter
import io.objectbox.relation.ToMany import io.objectbox.relation.ToMany
@Entity @Entity
class RoomEntity { class RoomEntity {
@Id var id: Long = 0 @Id var id: Long = 0
@Convert(converter = MembershipConverter::class, dbType = String::class)
var membership: Membership = Membership.NONE
lateinit var roomId: String lateinit var roomId: String
lateinit var chunks: ToMany<ChunkEntity> lateinit var chunks: ToMany<ChunkEntity>
companion object;
enum class Membership {
JOINED,
LEFT,
INVITED,
NONE
}
}
class MembershipConverter : PropertyConverter<RoomEntity.Membership, String> {
override fun convertToDatabaseValue(entityProperty: RoomEntity.Membership?): String? {
return entityProperty?.name
}
override fun convertToEntityProperty(databaseValue: String?): RoomEntity.Membership? {
return databaseValue?.let { RoomEntity.Membership.valueOf(databaseValue) }
}
} }

View File

@ -0,0 +1,21 @@
package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.model.RoomEntity_
import io.objectbox.Box
fun RoomEntity.Companion.getForId(roomBox: Box<RoomEntity>, roomId: String): RoomEntity? {
return roomBox
.query()
.equal(RoomEntity_.roomId, roomId)
.build()
.findUnique()
}
fun RoomEntity.Companion.getAll(roomBox: Box<RoomEntity>, membership: RoomEntity.Membership? = null): List<RoomEntity> {
val query = roomBox.query()
if (membership != null) {
query.filter { it.membership == membership }
}
return query.build().find()
}

View File

@ -1,9 +1,61 @@
package im.vector.matrix.android.internal.events.sync package im.vector.matrix.android.internal.events.sync
class RoomSyncHandler() { import im.vector.matrix.android.internal.database.mapper.EventMapper
import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.getForId
import im.vector.matrix.android.internal.events.sync.data.RoomSync
import io.objectbox.Box
import io.objectbox.BoxStore
class RoomSyncHandler(
boxStore: BoxStore
) {
private val eventBox: Box<EventEntity> = boxStore.boxFor(EventEntity::class.java)
private val chunkBox: Box<ChunkEntity> = boxStore.boxFor(ChunkEntity::class.java)
private val roomBox: Box<RoomEntity> = boxStore.boxFor(RoomEntity::class.java)
fun handleJoinedRooms(roomSyncByRoom: Map<String, RoomSync>?) {
if (roomSyncByRoom == null) {
return
}
val roomEntities = ArrayList<RoomEntity>()
roomSyncByRoom.forEach { roomId, roomSync ->
val roomEntity = handleJoinedRoom(roomId, roomSync)
roomEntities.add(roomEntity)
}
roomBox.put(roomEntities)
}
private fun handleJoinedRoom(roomId: String, roomSync: RoomSync): RoomEntity {
val roomEntity = RoomEntity.getForId(roomBox, roomId) ?: RoomEntity().apply { this.roomId = roomId }
if (roomEntity.membership == RoomEntity.Membership.INVITED) {
roomEntity.chunks
.map { it.events }
.forEach { eventBox.remove(it) }
chunkBox.remove(roomEntity.chunks)
}
roomEntity.membership = RoomEntity.Membership.JOINED
if (roomSync.timeline != null) {
val chunkEntity = ChunkEntity()
chunkEntity.prevToken = roomSync.timeline.prevBatch
roomSync.timeline.events
.map { event -> EventMapper.map(event) }
.forEach { chunkEntity.events.add(it) }
roomEntity.chunks.add(chunkEntity)
}
if (roomSync.state != null) {
val chunkEntity = ChunkEntity()
roomSync.state.events
.map { event -> EventMapper.map(event) }
.forEach { chunkEntity.events.add(it) }
roomEntity.chunks.add(chunkEntity)
}
return roomEntity
}
} }

View File

@ -16,7 +16,11 @@ class SyncModule : Module {
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
SyncResponseHandler(get()) RoomSyncHandler(get())
}
scope(DefaultSession.SCOPE) {
SyncResponseHandler(get(), get())
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {

View File

@ -8,21 +8,14 @@ import im.vector.matrix.android.internal.legacy.data.Room
import im.vector.matrix.android.internal.legacy.data.store.IMXStore import im.vector.matrix.android.internal.legacy.data.store.IMXStore
import im.vector.matrix.android.internal.legacy.data.store.MXMemoryStore import im.vector.matrix.android.internal.legacy.data.store.MXMemoryStore
import im.vector.matrix.android.internal.legacy.rest.client.AccountDataRestClient import im.vector.matrix.android.internal.legacy.rest.client.AccountDataRestClient
import im.vector.matrix.android.internal.legacy.rest.model.RoomMember
import im.vector.matrix.android.internal.legacy.rest.model.bingrules.PushRulesResponse import im.vector.matrix.android.internal.legacy.rest.model.bingrules.PushRulesResponse
import im.vector.matrix.android.internal.legacy.util.JsonUtils import im.vector.matrix.android.internal.legacy.util.JsonUtils
import timber.log.Timber import timber.log.Timber
import java.util.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.List
import kotlin.collections.Map
import kotlin.collections.MutableList
import kotlin.collections.MutableMap
import kotlin.collections.emptyList
import kotlin.collections.isNotEmpty
import kotlin.collections.set
class SyncResponseHandler(private val dataHandler: MXDataHandler) { class SyncResponseHandler(
private val roomSyncHandler: RoomSyncHandler,
private val dataHandler: MXDataHandler
) {
private val store = dataHandler.store private val store = dataHandler.store
private val leftRoomsStore = MXMemoryStore() private val leftRoomsStore = MXMemoryStore()
@ -34,6 +27,13 @@ class SyncResponseHandler(private val dataHandler: MXDataHandler) {
return return
} }
Timber.v("Handle sync response") Timber.v("Handle sync response")
if (syncResponse.rooms != null) {
// joined rooms events
roomSyncHandler.handleJoinedRooms(syncResponse.rooms.join)
}
/*
val isInitialSync = null == fromToken val isInitialSync = null == fromToken
var isEmptyResponse = true var isEmptyResponse = true
@ -50,21 +50,8 @@ class SyncResponseHandler(private val dataHandler: MXDataHandler) {
manageAccountData(syncResponse.accountData, isInitialSync) manageAccountData(syncResponse.accountData, isInitialSync)
} }
// sanity check
if (syncResponse.rooms != null) { if (syncResponse.rooms != null) {
// joined rooms events
if (syncResponse.rooms.join.isNotEmpty()) {
val roomIds = syncResponse.rooms.join.keys
// Handle first joined rooms
for (roomId in roomIds) {
if (null != leftRoomsStore.getRoom(roomId)) {
leftRoomsStore.deleteRoom(roomId)
}
// TODO handle joined room
//getRoom(roomId).handleJoinedRoomSync(syncResponse.rooms.join[roomId], isInitialSync)
}
isEmptyResponse = false
}
// invited room management // invited room management
if (syncResponse.rooms.invite.isNotEmpty()) { if (syncResponse.rooms.invite.isNotEmpty()) {
@ -117,131 +104,137 @@ class SyncResponseHandler(private val dataHandler: MXDataHandler) {
} }
} }
} }
}
isEmptyResponse = false
if (hasChanged) {
// Update account data to add new direct chat room(s)
/* mAccountDataRestClient.setAccountData(mCredentials.userId, AccountDataRestClient.ACCOUNT_DATA_TYPE_DIRECT_MESSAGES,
updatedDirectChatRoomsDict, object : ApiCallback<Void> {
override fun onSuccess(info: Void) {
}
override fun onNetworkError(e: Exception) {
// TODO: we should try again.
}
override fun onMatrixError(e: MatrixError) {
}
override fun onUnexpectedError(e: Exception) {
}
})*/
} }
} }
// left room management isEmptyResponse = false
// it should be done at the end but it seems there is a server issue
// when inviting after leaving a room, the room is defined in the both leave & invite rooms list.
if (syncResponse.rooms.leave.isNotEmpty()) {
val roomIds = syncResponse.rooms.leave.keys
for (roomId in roomIds) {
// RoomSync leftRoomSync = syncResponse.rooms.leave.get(roomId);
// Presently we remove the existing room from the rooms list. if (hasChanged) {
// FIXME SYNC V2 Archive/Display the left rooms! // Update account data to add new direct chat room(s)
// For that create 'handleArchivedRoomSync' method /* mAccountDataRestClient.setAccountData(mCredentials.userId, AccountDataRestClient.ACCOUNT_DATA_TYPE_DIRECT_MESSAGES,
updatedDirectChatRoomsDict, object : ApiCallback<Void> {
override fun onSuccess(info: Void) {
}
var membership = RoomMember.MEMBERSHIP_LEAVE override fun onNetworkError(e: Exception) {
val room = getRoom(roomId) // TODO: we should try again.
}
// Retrieve existing room override fun onMatrixError(e: MatrixError) {
// The room will then be able to notify its listeners. }
// TODO handle
// room.handleJoinedRoomSync(syncResponse.rooms.leave[roomId], isInitialSync)
val member = room.getMember(dataHandler.userId) override fun onUnexpectedError(e: Exception) {
if (null != member) { }
membership = member.membership })*/
}
if (!TextUtils.equals(membership, RoomMember.MEMBERSHIP_KICK) && !TextUtils.equals(membership, RoomMember.MEMBERSHIP_BAN)) {
// ensure that the room data are properly deleted
store.deleteRoom(roomId)
dataHandler.onLeaveRoom(roomId)
} else {
dataHandler.onRoomKick(roomId)
}
// don't add to the left rooms if the user has been kicked / banned
if (areLeftRoomsSynced && TextUtils.equals(membership, RoomMember.MEMBERSHIP_LEAVE)) {
val leftRoom = getRoom(leftRoomsStore, roomId, true)
//Todo handle
//leftRoom.handleJoinedRoomSync(syncResponse.rooms.leave[roomId], isInitialSync)
}
}
isEmptyResponse = false
} }
} }
// groups // left room management
if (null != syncResponse.groups) { // it should be done at the end but it seems there is a server issue
// when inviting after leaving a room, the room is defined in the both leave & invite rooms list.
if (syncResponse.rooms.leave.isNotEmpty()) {
val roomIds = syncResponse.rooms.leave.keys
for (roomId in roomIds) {
// RoomSync leftRoomSync = syncResponse.rooms.leave.get(roomId);
// Presently we remove the existing room from the rooms list.
// FIXME SYNC V2 Archive/Display the left rooms!
// For that create 'handleArchivedRoomSync' method
var membership = RoomMember.MEMBERSHIP_LEAVE
val room = getRoom(roomId)
// Retrieve existing room
// The room will then be able to notify its listeners.
// TODO handle
// room.handleJoinedRoomSync(syncResponse.rooms.leave[roomId], isInitialSync)
val member = room.getMember(dataHandler.userId)
if (null != member) {
membership = member.membership
}
if (!TextUtils.equals(membership, RoomMember.MEMBERSHIP_KICK) && !TextUtils.equals(membership, RoomMember.MEMBERSHIP_BAN)) {
// ensure that the room data are properly deleted
store.deleteRoom(roomId)
dataHandler.onLeaveRoom(roomId)
} else {
dataHandler.onRoomKick(roomId)
}
// don't add to the left rooms if the user has been kicked / banned
if (areLeftRoomsSynced && TextUtils.equals(membership, RoomMember.MEMBERSHIP_LEAVE)) {
val leftRoom = getRoom(leftRoomsStore, roomId, true)
//Todo handle
//leftRoom.handleJoinedRoomSync(syncResponse.rooms.leave[roomId], isInitialSync)
}
}
isEmptyResponse = false
}
}
// groups
if (null != syncResponse.groups)
{
// Handle invited groups
if (null != syncResponse.groups.invite && !syncResponse.groups.invite.isEmpty()) {
// Handle invited groups // Handle invited groups
if (null != syncResponse.groups.invite && !syncResponse.groups.invite.isEmpty()) { for (groupId in syncResponse.groups.invite.keys) {
// Handle invited groups val invitedGroupSync = syncResponse.groups.invite[groupId]
for (groupId in syncResponse.groups.invite.keys) { dataHandler.groupsManager.onNewGroupInvitation(groupId, invitedGroupSync?.profile, invitedGroupSync?.inviter, !isInitialSync)
val invitedGroupSync = syncResponse.groups.invite[groupId]
dataHandler.groupsManager.onNewGroupInvitation(groupId, invitedGroupSync?.profile, invitedGroupSync?.inviter, !isInitialSync)
}
} }
}
// Handle joined groups
if (null != syncResponse.groups.join && !syncResponse.groups.join.isEmpty()) {
for (groupId in syncResponse.groups.join.keys) {
dataHandler.groupsManager.onJoinGroup(groupId, !isInitialSync)
}
}
// Handle left groups
if (null != syncResponse.groups.leave && !syncResponse.groups.leave.isEmpty()) {
// Handle joined groups // Handle joined groups
if (null != syncResponse.groups.join && !syncResponse.groups.join.isEmpty()) { for (groupId in syncResponse.groups.leave.keys) {
for (groupId in syncResponse.groups.join.keys) { dataHandler.groupsManager.onLeaveGroup(groupId, !isInitialSync)
dataHandler.groupsManager.onJoinGroup(groupId, !isInitialSync)
}
}
// Handle left groups
if (null != syncResponse.groups.leave && !syncResponse.groups.leave.isEmpty()) {
// Handle joined groups
for (groupId in syncResponse.groups.leave.keys) {
dataHandler.groupsManager.onLeaveGroup(groupId, !isInitialSync)
}
} }
} }
}
// Handle presence of other users // Handle presence of other users
if (syncResponse.presence?.events != null) { if (syncResponse.presence?.events != null)
for (presenceEvent in syncResponse.presence.events) { {
handlePresenceEvent(presenceEvent) for (presenceEvent in syncResponse.presence.events) {
} handlePresenceEvent(presenceEvent)
}
dataHandler.crypto?.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
if (!isEmptyResponse) {
store.eventStreamToken = syncResponse.nextBatch
store.commit()
} }
}
dataHandler.crypto?.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
if (!isEmptyResponse)
{
store.eventStreamToken = syncResponse.nextBatch
store.commit()
}
if (isInitialSync) { if (isInitialSync)
if (!isCatchingUp) { {
dataHandler.startCrypto(true) if (!isCatchingUp) {
} else { dataHandler.startCrypto(true)
// the events thread sends a dummy initial sync event
// when the application is restarted.
isStartingCryptoWithInitialSync = !isEmptyResponse
}
dataHandler.onInitialSyncComplete(syncResponse?.nextBatch)
} else { } else {
// the events thread sends a dummy initial sync event
if (!isCatchingUp) { // when the application is restarted.
dataHandler.startCrypto(isStartingCryptoWithInitialSync) isStartingCryptoWithInitialSync = !isEmptyResponse
}
dataHandler.onLiveEventsChunkProcessed(fromToken, syncResponse.nextBatch)
dataHandler.callsManager?.checkPendingIncomingCalls()
} }
dataHandler.onInitialSyncComplete(syncResponse?.nextBatch)
} else
{
if (!isCatchingUp) {
dataHandler.startCrypto(isStartingCryptoWithInitialSync)
}
dataHandler.onLiveEventsChunkProcessed(fromToken, syncResponse.nextBatch)
dataHandler.callsManager?.checkPendingIncomingCalls()
}
*/
} }
private fun manageAccountData(accountData: Map<String, Any>, isInitialSync: Boolean) { private fun manageAccountData(accountData: Map<String, Any>, isInitialSync: Boolean) {

View File

@ -1,21 +1,7 @@
/*
* Copyright 2016 OpenMarket 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.events.sync.data package im.vector.matrix.android.internal.events.sync.data
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import im.vector.matrix.android.api.events.Event import im.vector.matrix.android.api.events.Event
@ -26,4 +12,5 @@ data class RoomSyncState(
/** /**
* List of state events (array of Event). The resulting state corresponds to the *start* of the timeline. * List of state events (array of Event). The resulting state corresponds to the *start* of the timeline.
*/ */
val events: List<Event>? = null) @Json(name = "events") val events: List<Event> = emptyList()
)

View File

@ -11,7 +11,7 @@ data class RoomSyncTimeline(
/** /**
* List of events (array of Event). * List of events (array of Event).
*/ */
val events: List<Event>? = null, val events: List<Event> = emptyList(),
/** /**
* Boolean which tells whether there are more events on the server * Boolean which tells whether there are more events on the server