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": []
},
{
"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",
"lastRelationId": "0:0",
"lastRelationId": "2:5944867505344991328",
"lastSequenceId": "0:0",
"modelVersion": 4,
"modelVersionParserMinimum": 4,

View File

@ -3,27 +3,6 @@
"_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.",
"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",
"lastPropertyId": "6:5675340775405504775",
@ -55,9 +34,29 @@
}
],
"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",
"lastRelationId": "0:0",
"lastSequenceId": "0:0",
@ -67,12 +66,14 @@
637433444018824383,
5577202525246066978,
213989653016909134,
826321009570992494
826321009570992494,
1775102368193732759
],
"retiredIndexUids": [
7573100044105293219,
7664899561635023422,
762622607983996029
762622607983996029,
1616715309690181473
],
"retiredPropertyUids": [
6211403495341530846,
@ -101,7 +102,10 @@
8488502568193639300,
594512591943458255,
5113574037182501000,
4929796643260285955
4929796643260285955,
5690571528511905880,
1811444397594387116,
8817556524159529201
],
"retiredRelationUids": [],
"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
class EventEntity {
@Id var id: Long = 0
lateinit var eventId: String
lateinit var type: String
lateinit var content: String
var eventId: String? = null
var prevContent: String? = null
var stateKey: String? = null
}

View File

@ -1,12 +1,37 @@
package im.vector.matrix.android.internal.database.model

import io.objectbox.annotation.Convert
import io.objectbox.annotation.Entity
import io.objectbox.annotation.Id
import io.objectbox.converter.PropertyConverter
import io.objectbox.relation.ToMany

@Entity
class RoomEntity {
@Id var id: Long = 0
@Convert(converter = MembershipConverter::class, dbType = String::class)
var membership: Membership = Membership.NONE
lateinit var roomId: String
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

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) {
SyncResponseHandler(get())
RoomSyncHandler(get())
}

scope(DefaultSession.SCOPE) {
SyncResponseHandler(get(), get())
}

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.MXMemoryStore
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.util.JsonUtils
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 leftRoomsStore = MXMemoryStore()
@ -34,6 +27,13 @@ class SyncResponseHandler(private val dataHandler: MXDataHandler) {
return
}
Timber.v("Handle sync response")

if (syncResponse.rooms != null) {
// joined rooms events
roomSyncHandler.handleJoinedRooms(syncResponse.rooms.join)
}

/*
val isInitialSync = null == fromToken
var isEmptyResponse = true

@ -50,21 +50,8 @@ class SyncResponseHandler(private val dataHandler: MXDataHandler) {
manageAccountData(syncResponse.accountData, isInitialSync)
}

// sanity check

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
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
// 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);
isEmptyResponse = false

// Presently we remove the existing room from the rooms list.
// FIXME SYNC V2 Archive/Display the left rooms!
// For that create 'handleArchivedRoomSync' method
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) {
}

var membership = RoomMember.MEMBERSHIP_LEAVE
val room = getRoom(roomId)
override fun onNetworkError(e: Exception) {
// TODO: we should try again.
}

// Retrieve existing room
// The room will then be able to notify its listeners.
// TODO handle
// room.handleJoinedRoomSync(syncResponse.rooms.leave[roomId], isInitialSync)
override fun onMatrixError(e: MatrixError) {
}

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
override fun onUnexpectedError(e: Exception) {
}
})*/
}
}

// groups
if (null != syncResponse.groups) {
// left room management
// 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
if (null != syncResponse.groups.invite && !syncResponse.groups.invite.isEmpty()) {
// Handle invited groups
for (groupId in syncResponse.groups.invite.keys) {
val invitedGroupSync = syncResponse.groups.invite[groupId]
dataHandler.groupsManager.onNewGroupInvitation(groupId, invitedGroupSync?.profile, invitedGroupSync?.inviter, !isInitialSync)
}
for (groupId in syncResponse.groups.invite.keys) {
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
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
for (groupId in syncResponse.groups.leave.keys) {
dataHandler.groupsManager.onLeaveGroup(groupId, !isInitialSync)
}
for (groupId in syncResponse.groups.leave.keys) {
dataHandler.groupsManager.onLeaveGroup(groupId, !isInitialSync)
}
}
}

// Handle presence of other users
if (syncResponse.presence?.events != null) {
for (presenceEvent in syncResponse.presence.events) {
handlePresenceEvent(presenceEvent)
}
}
dataHandler.crypto?.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
if (!isEmptyResponse) {
store.eventStreamToken = syncResponse.nextBatch
store.commit()
// Handle presence of other users
if (syncResponse.presence?.events != null)
{
for (presenceEvent in syncResponse.presence.events) {
handlePresenceEvent(presenceEvent)
}
}
dataHandler.crypto?.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
if (!isEmptyResponse)
{
store.eventStreamToken = syncResponse.nextBatch
store.commit()
}

if (isInitialSync) {
if (!isCatchingUp) {
dataHandler.startCrypto(true)
} else {
// the events thread sends a dummy initial sync event
// when the application is restarted.
isStartingCryptoWithInitialSync = !isEmptyResponse
}

dataHandler.onInitialSyncComplete(syncResponse?.nextBatch)
if (isInitialSync)
{
if (!isCatchingUp) {
dataHandler.startCrypto(true)
} else {

if (!isCatchingUp) {
dataHandler.startCrypto(isStartingCryptoWithInitialSync)
}

dataHandler.onLiveEventsChunkProcessed(fromToken, syncResponse.nextBatch)
dataHandler.callsManager?.checkPendingIncomingCalls()

// the events thread sends a dummy initial sync event
// when the application is restarted.
isStartingCryptoWithInitialSync = !isEmptyResponse
}

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) {

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


import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
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.
*/
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).
*/
val events: List<Event>? = null,
val events: List<Event> = emptyList(),

/**
* Boolean which tells whether there are more events on the server