Start to insert room summaries and listen for changes
This commit is contained in:
parent
b79d23ad24
commit
4904ac894e
Binary file not shown.
|
@ -28,6 +28,8 @@ dependencies {
|
||||||
implementation 'com.android.support:appcompat-v7:28.0.0'
|
implementation 'com.android.support:appcompat-v7:28.0.0'
|
||||||
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
|
||||||
|
|
||||||
|
implementation 'com.jakewharton.timber:timber:4.7.1'
|
||||||
|
|
||||||
implementation "org.koin:koin-android:$koin_version"
|
implementation "org.koin:koin-android:$koin_version"
|
||||||
implementation "org.koin:koin-android-scope:$koin_version"
|
implementation "org.koin:koin-android-scope:$koin_version"
|
||||||
implementation "org.koin:koin-android-viewmodel:$koin_version"
|
implementation "org.koin:koin-android-viewmodel:$koin_version"
|
||||||
|
|
|
@ -1,13 +1,18 @@
|
||||||
package im.vector.riotredesign
|
package im.vector.riotredesign
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
|
import im.vector.matrix.android.BuildConfig
|
||||||
import im.vector.riotredesign.core.di.AppModule
|
import im.vector.riotredesign.core.di.AppModule
|
||||||
import org.koin.standalone.StandAloneContext.startKoin
|
import org.koin.standalone.StandAloneContext.startKoin
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class Riot : Application() {
|
class Riot : Application() {
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
|
if (BuildConfig.DEBUG) {
|
||||||
|
Timber.plant(Timber.DebugTree())
|
||||||
|
}
|
||||||
startKoin(listOf(AppModule(this)))
|
startKoin(listOf(AppModule(this)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,24 +4,22 @@ import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.widget.Toast
|
|
||||||
import im.vector.matrix.android.api.Matrix
|
import im.vector.matrix.android.api.Matrix
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
import im.vector.matrix.android.internal.events.sync.data.SyncResponse
|
import im.vector.matrix.android.internal.events.sync.data.SyncResponse
|
||||||
import im.vector.riotredesign.R
|
import im.vector.riotredesign.R
|
||||||
import im.vector.riotredesign.core.platform.RiotActivity
|
import im.vector.riotredesign.core.platform.RiotActivity
|
||||||
import io.realm.RealmChangeListener
|
|
||||||
import io.realm.RealmResults
|
|
||||||
import kotlinx.android.synthetic.main.activity_home.*
|
import kotlinx.android.synthetic.main.activity_home.*
|
||||||
import org.koin.android.ext.android.inject
|
import org.koin.android.ext.android.inject
|
||||||
|
import timber.log.Timber
|
||||||
|
|
||||||
class HomeActivity : RiotActivity() {
|
class HomeActivity : RiotActivity() {
|
||||||
|
|
||||||
private val matrix by inject<Matrix>()
|
private val matrix by inject<Matrix>()
|
||||||
private val synchronizer = matrix.currentSession?.synchronizer()
|
private val synchronizer = matrix.currentSession?.synchronizer()
|
||||||
private val realmHolder = matrix.currentSession?.realmInstanceHolder()
|
private val realmHolder = matrix.currentSession?.realmHolder()
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
@ -33,24 +31,24 @@ class HomeActivity : RiotActivity() {
|
||||||
synchronizeButton.visibility = View.GONE
|
synchronizeButton.visibility = View.GONE
|
||||||
loadingView.visibility = View.VISIBLE
|
loadingView.visibility = View.VISIBLE
|
||||||
synchronizer?.synchronize(object : MatrixCallback<SyncResponse> {
|
synchronizer?.synchronize(object : MatrixCallback<SyncResponse> {
|
||||||
override fun onSuccess(data: SyncResponse?) {
|
override fun onSuccess(data: SyncResponse) {
|
||||||
synchronizeButton.visibility = View.VISIBLE
|
synchronizeButton.visibility = View.VISIBLE
|
||||||
loadingView.visibility = View.GONE
|
loadingView.visibility = View.GONE
|
||||||
|
Timber.v("Sync successful")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Failure) {
|
override fun onFailure(failure: Failure) {
|
||||||
synchronizeButton.visibility = View.VISIBLE
|
synchronizeButton.visibility = View.VISIBLE
|
||||||
loadingView.visibility = View.GONE
|
loadingView.visibility = View.GONE
|
||||||
Toast.makeText(this@HomeActivity, failure.toString(), Toast.LENGTH_LONG).show()
|
Timber.e("Sync has failed : %s", failure.toString())
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
if (realmHolder != null) {
|
if (realmHolder != null) {
|
||||||
val results = realmHolder.realm.where(EventEntity::class.java).equalTo("chunk.room.roomId", "!UlckfcnwgLKswCmUbe:matrix.org").findAll()
|
val results = realmHolder.instance.where(RoomSummaryEntity::class.java).findAll()
|
||||||
results.addChangeListener(RealmChangeListener<RealmResults<EventEntity>> {
|
results.addChangeListener { summaries ->
|
||||||
Toast.makeText(this@HomeActivity, "Room events data changed", Toast.LENGTH_LONG).show()
|
Timber.v("Summaries updated")
|
||||||
})
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -22,8 +22,8 @@ class LoginActivity : RiotActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
setContentView(R.layout.activity_login)
|
setContentView(R.layout.activity_login)
|
||||||
authenticateButton.setOnClickListener { authenticate() }
|
|
||||||
checkActiveSessions()
|
checkActiveSessions()
|
||||||
|
authenticateButton.setOnClickListener { authenticate() }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun authenticate() {
|
private fun authenticate() {
|
||||||
|
@ -37,9 +37,8 @@ class LoginActivity : RiotActivity() {
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
authenticator.authenticate(homeServerConnectionConfig, login, password, object : MatrixCallback<Session> {
|
authenticator.authenticate(homeServerConnectionConfig, login, password, object : MatrixCallback<Session> {
|
||||||
override fun onSuccess(data: Session?) {
|
override fun onSuccess(data: Session) {
|
||||||
matrix.currentSession = data
|
openSessionAndGoToHome(data)
|
||||||
goToHomeScreen()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onFailure(failure: Failure) {
|
override fun onFailure(failure: Failure) {
|
||||||
|
@ -51,12 +50,16 @@ class LoginActivity : RiotActivity() {
|
||||||
|
|
||||||
private fun checkActiveSessions() {
|
private fun checkActiveSessions() {
|
||||||
if (authenticator.hasActiveSessions()) {
|
if (authenticator.hasActiveSessions()) {
|
||||||
matrix.currentSession = authenticator.getLastActiveSession()
|
val session = authenticator.getLastActiveSession()
|
||||||
goToHomeScreen()
|
session?.let {
|
||||||
|
openSessionAndGoToHome(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun goToHomeScreen() {
|
private fun openSessionAndGoToHome(session: Session) {
|
||||||
|
matrix.currentSession = session
|
||||||
|
session.open()
|
||||||
val intent = HomeActivity.newIntent(this)
|
val intent = HomeActivity.newIntent(this)
|
||||||
startActivity(intent)
|
startActivity(intent)
|
||||||
finish()
|
finish()
|
||||||
|
|
|
@ -44,6 +44,7 @@ dependencies {
|
||||||
def arrow_version = "0.7.3"
|
def arrow_version = "0.7.3"
|
||||||
def support_version = '28.0.0'
|
def support_version = '28.0.0'
|
||||||
def moshi_version = '1.7.0'
|
def moshi_version = '1.7.0'
|
||||||
|
def lifecycle_version = "1.1.1"
|
||||||
|
|
||||||
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
implementation fileTree(dir: 'libs', include: ['*.aar'])
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
@ -51,6 +52,10 @@ dependencies {
|
||||||
implementation "com.android.support:appcompat-v7:$support_version"
|
implementation "com.android.support:appcompat-v7:$support_version"
|
||||||
implementation "com.android.support:recyclerview-v7:$support_version"
|
implementation "com.android.support:recyclerview-v7:$support_version"
|
||||||
|
|
||||||
|
implementation "android.arch.lifecycle:extensions:$lifecycle_version"
|
||||||
|
kapt "android.arch.lifecycle:compiler:$lifecycle_version"
|
||||||
|
|
||||||
|
|
||||||
// Network
|
// Network
|
||||||
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
|
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
|
||||||
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
implementation 'com.squareup.retrofit2:converter-moshi:2.4.0'
|
||||||
|
|
|
@ -4,7 +4,7 @@ import im.vector.matrix.android.api.failure.Failure
|
||||||
|
|
||||||
interface MatrixCallback<in T> {
|
interface MatrixCallback<in T> {
|
||||||
|
|
||||||
fun onSuccess(data: T?)
|
fun onSuccess(data: T)
|
||||||
|
|
||||||
fun onFailure(failure: Failure)
|
fun onFailure(failure: Failure)
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ data class Event(
|
||||||
@Json(name = "sender") val sender: String? = null,
|
@Json(name = "sender") val sender: String? = null,
|
||||||
@Json(name = "state_key") val stateKey: String? = null,
|
@Json(name = "state_key") val stateKey: String? = null,
|
||||||
@Json(name = "room_id") var roomId: String? = null,
|
@Json(name = "room_id") var roomId: String? = null,
|
||||||
@Json(name = "unsigned_data") val unsignedData: UnsignedData? = null
|
@Json(name = "unsigned") val unsignedData: UnsignedData? = null
|
||||||
) {
|
) {
|
||||||
|
|
||||||
val contentAsJsonObject: JsonObject? by lazy {
|
val contentAsJsonObject: JsonObject? by lazy {
|
||||||
|
|
|
@ -5,7 +5,7 @@ import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
@JsonClass(generateAdapter = true)
|
@JsonClass(generateAdapter = true)
|
||||||
data class UnsignedData(
|
data class UnsignedData(
|
||||||
@Json(name = "age") val age: Int,
|
@Json(name = "age") val age: Long?,
|
||||||
@Json(name = "redacted_because") val redactedEvent: Event? = null,
|
@Json(name = "redacted_because") val redactedEvent: Event? = null,
|
||||||
@Json(name = "transaction_id") val transactionId: String
|
@Json(name = "transaction_id") val transactionId: String? = null
|
||||||
)
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
package im.vector.matrix.android.api.rooms
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class RoomNameContent(
|
||||||
|
@Json(name = "name") val name: String
|
||||||
|
)
|
|
@ -0,0 +1,9 @@
|
||||||
|
package im.vector.matrix.android.api.rooms
|
||||||
|
|
||||||
|
import com.squareup.moshi.Json
|
||||||
|
import com.squareup.moshi.JsonClass
|
||||||
|
|
||||||
|
@JsonClass(generateAdapter = true)
|
||||||
|
data class RoomTopicContent(
|
||||||
|
@Json(name = "topic") val topic: String
|
||||||
|
)
|
|
@ -1,15 +1,20 @@
|
||||||
package im.vector.matrix.android.api.session
|
package im.vector.matrix.android.api.session
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.database.RealmInstanceHolder
|
import android.support.annotation.MainThread
|
||||||
|
import im.vector.matrix.android.internal.database.SessionRealmHolder
|
||||||
import im.vector.matrix.android.internal.events.sync.Synchronizer
|
import im.vector.matrix.android.internal.events.sync.Synchronizer
|
||||||
|
|
||||||
interface Session {
|
interface Session {
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
fun open()
|
||||||
|
|
||||||
fun synchronizer(): Synchronizer
|
fun synchronizer(): Synchronizer
|
||||||
|
|
||||||
// Visible for testing request directly. Will be deleted
|
// Visible for testing request directly. Will be deleted
|
||||||
fun realmInstanceHolder(): RealmInstanceHolder
|
fun realmHolder(): SessionRealmHolder
|
||||||
|
|
||||||
|
@MainThread
|
||||||
fun close()
|
fun close()
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,11 +0,0 @@
|
||||||
package im.vector.matrix.android.internal.database
|
|
||||||
|
|
||||||
import io.realm.Realm
|
|
||||||
import io.realm.RealmConfiguration
|
|
||||||
|
|
||||||
class RealmInstanceHolder(realmConfiguration: RealmConfiguration) {
|
|
||||||
|
|
||||||
val realm = Realm.getInstance(realmConfiguration)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
package im.vector.matrix.android.internal.database
|
||||||
|
|
||||||
|
import android.support.annotation.MainThread
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
|
||||||
|
class SessionRealmHolder(private val realmConfiguration: RealmConfiguration
|
||||||
|
) {
|
||||||
|
|
||||||
|
lateinit var instance: Realm
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
fun open() {
|
||||||
|
instance = Realm.getInstance(realmConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
fun close() {
|
||||||
|
instance.close()
|
||||||
|
Realm.compactRealm(realmConfiguration)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ package im.vector.matrix.android.internal.database.mapper
|
||||||
|
|
||||||
import com.squareup.moshi.Types
|
import com.squareup.moshi.Types
|
||||||
import im.vector.matrix.android.api.events.Event
|
import im.vector.matrix.android.api.events.Event
|
||||||
|
import im.vector.matrix.android.api.events.UnsignedData
|
||||||
import im.vector.matrix.android.internal.database.model.EventEntity
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
import im.vector.matrix.android.internal.di.MoshiProvider
|
import im.vector.matrix.android.internal.di.MoshiProvider
|
||||||
|
|
||||||
|
@ -12,14 +13,38 @@ object EventMapper {
|
||||||
private val type = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)
|
private val type = Types.newParameterizedType(Map::class.java, String::class.java, Any::class.java)
|
||||||
private val adapter = moshi.adapter<Map<String, Any>>(type)
|
private val adapter = moshi.adapter<Map<String, Any>>(type)
|
||||||
|
|
||||||
fun map(event: Event): EventEntity {
|
internal fun map(event: Event): EventEntity {
|
||||||
val eventEntity = EventEntity()
|
val eventEntity = EventEntity()
|
||||||
eventEntity.eventId = event.eventId!!
|
eventEntity.eventId = event.eventId!!
|
||||||
eventEntity.content = adapter.toJson(event.content)
|
eventEntity.content = adapter.toJson(event.content)
|
||||||
eventEntity.prevContent = adapter.toJson(event.prevContent)
|
eventEntity.prevContent = adapter.toJson(event.prevContent)
|
||||||
eventEntity.stateKey = event.stateKey
|
eventEntity.stateKey = event.stateKey
|
||||||
eventEntity.type = event.type
|
eventEntity.type = event.type
|
||||||
|
eventEntity.sender = event.sender
|
||||||
|
eventEntity.originServerTs = event.originServerTs
|
||||||
|
eventEntity.age = event.unsignedData?.age ?: event.originServerTs
|
||||||
return eventEntity
|
return eventEntity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun map(eventEntity: EventEntity): Event {
|
||||||
|
return Event(
|
||||||
|
eventEntity.type,
|
||||||
|
eventEntity.eventId,
|
||||||
|
adapter.fromJson(eventEntity.content),
|
||||||
|
adapter.fromJson(eventEntity.prevContent ?: ""),
|
||||||
|
eventEntity.originServerTs,
|
||||||
|
eventEntity.sender,
|
||||||
|
eventEntity.stateKey,
|
||||||
|
null,
|
||||||
|
UnsignedData(eventEntity.age)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun EventEntity.asDomain(): Event {
|
||||||
|
return EventMapper.map(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Event.asEntity(): EventEntity {
|
||||||
|
return EventMapper.map(this)
|
||||||
}
|
}
|
|
@ -9,9 +9,14 @@ open class EventEntity(@PrimaryKey var eventId: String = "",
|
||||||
var type: String = "",
|
var type: String = "",
|
||||||
var content: String = "",
|
var content: String = "",
|
||||||
var prevContent: String? = null,
|
var prevContent: String? = null,
|
||||||
var stateKey: String? = null
|
var stateKey: String? = null,
|
||||||
|
var originServerTs: Long? = null,
|
||||||
|
var sender: String? = null,
|
||||||
|
var age: Long? = 0
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
|
companion object
|
||||||
|
|
||||||
@LinkingObjects("events")
|
@LinkingObjects("events")
|
||||||
val chunk: RealmResults<ChunkEntity>? = null
|
val chunk: RealmResults<ChunkEntity>? = null
|
||||||
|
|
||||||
|
|
|
@ -6,10 +6,10 @@ import io.realm.annotations.Ignore
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
import kotlin.properties.Delegates
|
import kotlin.properties.Delegates
|
||||||
|
|
||||||
open class RoomEntity : RealmObject() {
|
open class RoomEntity(@PrimaryKey var roomId: String = "",
|
||||||
|
|
||||||
@PrimaryKey var roomId: String = ""
|
|
||||||
var chunks: RealmList<ChunkEntity> = RealmList()
|
var chunks: RealmList<ChunkEntity> = RealmList()
|
||||||
|
) : RealmObject() {
|
||||||
|
|
||||||
private var membershipStr: String = Membership.NONE.name
|
private var membershipStr: String = Membership.NONE.name
|
||||||
|
|
||||||
@delegate:Ignore var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue ->
|
@delegate:Ignore var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue ->
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package im.vector.matrix.android.internal.database.model
|
||||||
|
|
||||||
|
import io.realm.RealmObject
|
||||||
|
import io.realm.annotations.PrimaryKey
|
||||||
|
|
||||||
|
// TODO to be completed
|
||||||
|
open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
|
||||||
|
var displayName: String? = "",
|
||||||
|
var topic: String? = "",
|
||||||
|
var lastMessage: EventEntity? = null
|
||||||
|
) : RealmObject()
|
|
@ -0,0 +1,20 @@
|
||||||
|
package im.vector.matrix.android.internal.database.query
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
|
import io.realm.Realm
|
||||||
|
|
||||||
|
fun ChunkEntity.Companion.getLastChunkFromRoom(realm: Realm, roomId: String): ChunkEntity? {
|
||||||
|
return realm.where(ChunkEntity::class.java)
|
||||||
|
.equalTo("room.roomId", roomId)
|
||||||
|
.isNull("nextToken")
|
||||||
|
.and()
|
||||||
|
.isNotNull("prevToken")
|
||||||
|
.findAll()
|
||||||
|
.lastOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ChunkEntity.Companion.getChunkIncludingEvents(realm: Realm, eventIds: List<String>): ChunkEntity? {
|
||||||
|
return realm.where(ChunkEntity::class.java)
|
||||||
|
.`in`("events.eventId", eventIds.toTypedArray())
|
||||||
|
.findFirst()
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
package im.vector.matrix.android.internal.database.query
|
||||||
|
|
||||||
|
import im.vector.matrix.android.internal.database.model.EventEntity
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmResults
|
||||||
|
|
||||||
|
fun EventEntity.Companion.getAllFromRoom(realm: Realm, roomId: String): RealmResults<EventEntity> {
|
||||||
|
return realm.where(EventEntity::class.java)
|
||||||
|
.equalTo("chunk.room.roomId", roomId)
|
||||||
|
.findAll()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun RealmResults<EventEntity>.getLast(type: String? = null): EventEntity? {
|
||||||
|
var query = this.where()
|
||||||
|
if (type != null) {
|
||||||
|
query = query.equalTo("type", type)
|
||||||
|
}
|
||||||
|
return query.findAll().sort("age").lastOrNull()
|
||||||
|
}
|
|
@ -2,6 +2,7 @@ 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.realm.Realm
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmResults
|
||||||
|
|
||||||
fun RoomEntity.Companion.getForId(realm: Realm, roomId: String): RoomEntity? {
|
fun RoomEntity.Companion.getForId(realm: Realm, roomId: String): RoomEntity? {
|
||||||
return realm.where<RoomEntity>(RoomEntity::class.java)
|
return realm.where<RoomEntity>(RoomEntity::class.java)
|
||||||
|
@ -9,10 +10,10 @@ fun RoomEntity.Companion.getForId(realm: Realm, roomId: String): RoomEntity? {
|
||||||
.findFirst()
|
.findFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun RoomEntity.Companion.getAll(realm: Realm, membership: RoomEntity.Membership? = null): List<RoomEntity> {
|
fun RoomEntity.Companion.getAllAsync(realm: Realm, membership: RoomEntity.Membership? = null): RealmResults<RoomEntity> {
|
||||||
val query = realm.where(RoomEntity::class.java)
|
val query = realm.where(RoomEntity::class.java)
|
||||||
if (membership != null) {
|
if (membership != null) {
|
||||||
query.equalTo("membership", membership.name)
|
query.equalTo("membership", membership.name)
|
||||||
}
|
}
|
||||||
return query.findAll()
|
return query.findAllAsync()
|
||||||
}
|
}
|
|
@ -3,7 +3,6 @@ package im.vector.matrix.android.internal.di
|
||||||
import im.vector.matrix.android.api.MatrixOptions
|
import im.vector.matrix.android.api.MatrixOptions
|
||||||
import im.vector.matrix.android.api.thread.MainThreadExecutor
|
import im.vector.matrix.android.api.thread.MainThreadExecutor
|
||||||
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
import io.realm.Realm
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.IO
|
import kotlinx.coroutines.IO
|
||||||
import kotlinx.coroutines.asCoroutineDispatcher
|
import kotlinx.coroutines.asCoroutineDispatcher
|
||||||
|
|
|
@ -1,9 +1,7 @@
|
||||||
package im.vector.matrix.android.internal.di
|
package im.vector.matrix.android.internal.di
|
||||||
|
|
||||||
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
|
import com.jakewharton.retrofit2.adapter.kotlin.coroutines.CoroutineCallAdapterFactory
|
||||||
import com.squareup.moshi.Moshi
|
|
||||||
import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
import im.vector.matrix.android.internal.network.AccessTokenInterceptor
|
||||||
import im.vector.matrix.android.internal.network.parsing.UriMoshiAdapter
|
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import okhttp3.logging.HttpLoggingInterceptor
|
import okhttp3.logging.HttpLoggingInterceptor
|
||||||
import org.koin.dsl.context.ModuleDefinition
|
import org.koin.dsl.context.ModuleDefinition
|
||||||
|
|
|
@ -1,11 +1,12 @@
|
||||||
package im.vector.matrix.android.internal.di
|
package im.vector.matrix.android.internal.di
|
||||||
|
|
||||||
import im.vector.matrix.android.internal.auth.data.SessionParams
|
import im.vector.matrix.android.internal.auth.data.SessionParams
|
||||||
import im.vector.matrix.android.internal.database.RealmInstanceHolder
|
import im.vector.matrix.android.internal.database.SessionRealmHolder
|
||||||
import im.vector.matrix.android.internal.legacy.MXDataHandler
|
import im.vector.matrix.android.internal.legacy.MXDataHandler
|
||||||
import im.vector.matrix.android.internal.legacy.MXSession
|
import im.vector.matrix.android.internal.legacy.MXSession
|
||||||
import im.vector.matrix.android.internal.legacy.data.store.MXFileStore
|
import im.vector.matrix.android.internal.legacy.data.store.MXFileStore
|
||||||
import im.vector.matrix.android.internal.session.DefaultSession
|
import im.vector.matrix.android.internal.session.DefaultSession
|
||||||
|
import im.vector.matrix.android.internal.session.RoomSummaryObserver
|
||||||
import io.realm.RealmConfiguration
|
import io.realm.RealmConfiguration
|
||||||
import org.koin.dsl.context.ModuleDefinition
|
import org.koin.dsl.context.ModuleDefinition
|
||||||
import org.koin.dsl.module.Module
|
import org.koin.dsl.module.Module
|
||||||
|
@ -21,7 +22,7 @@ class SessionModule(private val sessionParams: SessionParams) : Module {
|
||||||
}
|
}
|
||||||
|
|
||||||
scope(DefaultSession.SCOPE) {
|
scope(DefaultSession.SCOPE) {
|
||||||
RealmInstanceHolder(get())
|
SessionRealmHolder(get())
|
||||||
}
|
}
|
||||||
|
|
||||||
scope(DefaultSession.SCOPE) {
|
scope(DefaultSession.SCOPE) {
|
||||||
|
@ -31,6 +32,10 @@ class SessionModule(private val sessionParams: SessionParams) : Module {
|
||||||
.build()
|
.build()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scope(DefaultSession.SCOPE) {
|
||||||
|
RoomSummaryObserver(get(), get(), get())
|
||||||
|
}
|
||||||
|
|
||||||
scope(DefaultSession.SCOPE) {
|
scope(DefaultSession.SCOPE) {
|
||||||
val store = MXFileStore(sessionParams.credentials, false, get())
|
val store = MXFileStore(sessionParams.credentials, false, get())
|
||||||
val dataHandler = MXDataHandler(store, sessionParams.credentials)
|
val dataHandler = MXDataHandler(store, sessionParams.credentials)
|
||||||
|
|
|
@ -1,10 +1,12 @@
|
||||||
package im.vector.matrix.android.internal.events.sync
|
package im.vector.matrix.android.internal.events.sync
|
||||||
|
|
||||||
import im.vector.matrix.android.api.events.Event
|
import im.vector.matrix.android.api.events.Event
|
||||||
import im.vector.matrix.android.internal.database.mapper.EventMapper
|
import im.vector.matrix.android.internal.database.mapper.asEntity
|
||||||
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
import im.vector.matrix.android.internal.database.model.ChunkEntity
|
||||||
import im.vector.matrix.android.internal.database.model.RoomEntity
|
import im.vector.matrix.android.internal.database.model.RoomEntity
|
||||||
|
import im.vector.matrix.android.internal.database.query.getChunkIncludingEvents
|
||||||
import im.vector.matrix.android.internal.database.query.getForId
|
import im.vector.matrix.android.internal.database.query.getForId
|
||||||
|
import im.vector.matrix.android.internal.database.query.getLastChunkFromRoom
|
||||||
import im.vector.matrix.android.internal.events.sync.data.InvitedRoomSync
|
import im.vector.matrix.android.internal.events.sync.data.InvitedRoomSync
|
||||||
import im.vector.matrix.android.internal.events.sync.data.RoomSync
|
import im.vector.matrix.android.internal.events.sync.data.RoomSync
|
||||||
import io.realm.Realm
|
import io.realm.Realm
|
||||||
|
@ -39,8 +41,7 @@ class RoomSyncHandler(private val realmConfiguration: RealmConfiguration) {
|
||||||
roomId: String,
|
roomId: String,
|
||||||
roomSync: RoomSync): RoomEntity {
|
roomSync: RoomSync): RoomEntity {
|
||||||
|
|
||||||
val roomEntity = RoomEntity.getForId(realm, roomId)
|
val roomEntity = RoomEntity.getForId(realm, roomId) ?: RoomEntity(roomId)
|
||||||
?: RoomEntity().apply { this.roomId = roomId }
|
|
||||||
|
|
||||||
if (roomEntity.membership == RoomEntity.Membership.INVITED) {
|
if (roomEntity.membership == RoomEntity.Membership.INVITED) {
|
||||||
roomEntity.chunks.deleteAllFromRealm()
|
roomEntity.chunks.deleteAllFromRealm()
|
||||||
|
@ -95,16 +96,16 @@ class RoomSyncHandler(private val realmConfiguration: RealmConfiguration) {
|
||||||
isLimited: Boolean = true): ChunkEntity {
|
isLimited: Boolean = true): ChunkEntity {
|
||||||
|
|
||||||
val chunkEntity = if (!isLimited) {
|
val chunkEntity = if (!isLimited) {
|
||||||
realm.where(ChunkEntity::class.java).equalTo("room.roomId", roomId).isNull("nextToken").and().isNotNull("prevToken").findAll().lastOrNull()
|
ChunkEntity.getLastChunkFromRoom(realm, roomId)
|
||||||
} else {
|
} else {
|
||||||
realm.where(ChunkEntity::class.java).`in`("events.eventId", eventList.map { it.eventId }.toTypedArray()).findFirst()
|
val eventIds = eventList.filter { it.eventId != null }.map { it.eventId!! }
|
||||||
} ?: ChunkEntity()
|
ChunkEntity.getChunkIncludingEvents(realm, eventIds)
|
||||||
|
} ?: ChunkEntity().apply { this.prevToken = prevToken }
|
||||||
|
|
||||||
chunkEntity.prevToken = prevToken
|
|
||||||
chunkEntity.nextToken = nextToken
|
chunkEntity.nextToken = nextToken
|
||||||
chunkEntity.isLimited = isLimited
|
chunkEntity.isLimited = isLimited
|
||||||
eventList.forEach { event ->
|
eventList.forEach { event ->
|
||||||
val eventEntity = EventMapper.map(event).let {
|
val eventEntity = event.asEntity().let {
|
||||||
realm.copyToRealmOrUpdate(it)
|
realm.copyToRealmOrUpdate(it)
|
||||||
}
|
}
|
||||||
if (!chunkEntity.events.contains(eventEntity)) {
|
if (!chunkEntity.events.contains(eventEntity)) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package im.vector.matrix.android.internal.events.sync
|
||||||
|
|
||||||
import arrow.core.Either
|
import arrow.core.Either
|
||||||
import arrow.core.flatMap
|
import arrow.core.flatMap
|
||||||
|
import arrow.core.leftIfNull
|
||||||
import im.vector.matrix.android.api.MatrixCallback
|
import im.vector.matrix.android.api.MatrixCallback
|
||||||
import im.vector.matrix.android.api.failure.Failure
|
import im.vector.matrix.android.api.failure.Failure
|
||||||
import im.vector.matrix.android.api.util.Cancelable
|
import im.vector.matrix.android.api.util.Cancelable
|
||||||
|
@ -42,6 +43,8 @@ class Synchronizer(private val syncAPI: SyncAPI,
|
||||||
params["filter"] = filterBody.toJSONString()
|
params["filter"] = filterBody.toJSONString()
|
||||||
executeRequest<SyncResponse> {
|
executeRequest<SyncResponse> {
|
||||||
apiCall = syncAPI.sync(params)
|
apiCall = syncAPI.sync(params)
|
||||||
|
}.leftIfNull {
|
||||||
|
Failure.Unknown(RuntimeException("Sync response shouln't be null"))
|
||||||
}.flatMap {
|
}.flatMap {
|
||||||
token = it?.nextBatch
|
token = it?.nextBatch
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -1,8 +1,10 @@
|
||||||
package im.vector.matrix.android.internal.session
|
package im.vector.matrix.android.internal.session
|
||||||
|
|
||||||
|
import android.os.Looper
|
||||||
|
import android.support.annotation.MainThread
|
||||||
import im.vector.matrix.android.api.session.Session
|
import im.vector.matrix.android.api.session.Session
|
||||||
import im.vector.matrix.android.internal.auth.data.SessionParams
|
import im.vector.matrix.android.internal.auth.data.SessionParams
|
||||||
import im.vector.matrix.android.internal.database.RealmInstanceHolder
|
import im.vector.matrix.android.internal.database.SessionRealmHolder
|
||||||
import im.vector.matrix.android.internal.di.SessionModule
|
import im.vector.matrix.android.internal.di.SessionModule
|
||||||
import im.vector.matrix.android.internal.events.sync.SyncModule
|
import im.vector.matrix.android.internal.events.sync.SyncModule
|
||||||
import im.vector.matrix.android.internal.events.sync.Synchronizer
|
import im.vector.matrix.android.internal.events.sync.Synchronizer
|
||||||
|
@ -12,34 +14,57 @@ import org.koin.standalone.StandAloneContext
|
||||||
import org.koin.standalone.getKoin
|
import org.koin.standalone.getKoin
|
||||||
import org.koin.standalone.inject
|
import org.koin.standalone.inject
|
||||||
|
|
||||||
class DefaultSession(sessionParams: SessionParams) : Session, KoinComponent {
|
class DefaultSession(private val sessionParams: SessionParams) : Session, KoinComponent {
|
||||||
|
|
||||||
private val realmInstanceHolder by inject<RealmInstanceHolder>()
|
|
||||||
private val synchronizer by inject<Synchronizer>()
|
|
||||||
private val scope: Scope
|
|
||||||
|
|
||||||
init {
|
|
||||||
val sessionModule = SessionModule(sessionParams)
|
|
||||||
val syncModule = SyncModule()
|
|
||||||
StandAloneContext.loadKoinModules(listOf(sessionModule, syncModule))
|
|
||||||
scope = getKoin().getOrCreateScope(SCOPE)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun synchronizer(): Synchronizer {
|
|
||||||
return synchronizer
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun realmInstanceHolder(): RealmInstanceHolder {
|
|
||||||
return realmInstanceHolder
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun close() {
|
|
||||||
realmInstanceHolder.realm.close()
|
|
||||||
scope.close()
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
const val SCOPE: String = "session"
|
const val SCOPE: String = "session"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private lateinit var scope: Scope
|
||||||
|
|
||||||
|
private val realmInstanceHolder by inject<SessionRealmHolder>()
|
||||||
|
private val synchronizer by inject<Synchronizer>()
|
||||||
|
private val roomSummaryObserver by inject<RoomSummaryObserver>()
|
||||||
|
|
||||||
|
private var isOpen = false
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
override fun open() {
|
||||||
|
checkIsMainThread()
|
||||||
|
assert(!isOpen)
|
||||||
|
isOpen = true
|
||||||
|
val sessionModule = SessionModule(sessionParams)
|
||||||
|
val syncModule = SyncModule()
|
||||||
|
StandAloneContext.loadKoinModules(listOf(sessionModule, syncModule))
|
||||||
|
scope = getKoin().getOrCreateScope(SCOPE)
|
||||||
|
realmInstanceHolder.open()
|
||||||
|
roomSummaryObserver.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun synchronizer(): Synchronizer {
|
||||||
|
assert(isOpen)
|
||||||
|
return synchronizer
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun realmHolder(): SessionRealmHolder {
|
||||||
|
assert(isOpen)
|
||||||
|
return realmInstanceHolder
|
||||||
|
}
|
||||||
|
|
||||||
|
@MainThread
|
||||||
|
override fun close() {
|
||||||
|
checkIsMainThread()
|
||||||
|
assert(isOpen)
|
||||||
|
roomSummaryObserver.dispose()
|
||||||
|
realmInstanceHolder.close()
|
||||||
|
scope.close()
|
||||||
|
isOpen = false
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkIsMainThread() {
|
||||||
|
if (Looper.myLooper() != Looper.getMainLooper()) {
|
||||||
|
throw IllegalStateException("Should be called on main thread")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
package im.vector.matrix.android.internal.session
|
||||||
|
|
||||||
|
import im.vector.matrix.android.api.events.EventType
|
||||||
|
import im.vector.matrix.android.api.rooms.RoomNameContent
|
||||||
|
import im.vector.matrix.android.api.rooms.RoomTopicContent
|
||||||
|
import im.vector.matrix.android.internal.database.SessionRealmHolder
|
||||||
|
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.RoomEntity
|
||||||
|
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
|
||||||
|
import im.vector.matrix.android.internal.database.query.getAllAsync
|
||||||
|
import im.vector.matrix.android.internal.database.query.getAllFromRoom
|
||||||
|
import im.vector.matrix.android.internal.database.query.getLast
|
||||||
|
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
|
||||||
|
import io.realm.Realm
|
||||||
|
import io.realm.RealmConfiguration
|
||||||
|
import io.realm.RealmResults
|
||||||
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean
|
||||||
|
|
||||||
|
internal class RoomSummaryObserver(private val mainThreadRealm: SessionRealmHolder,
|
||||||
|
private val matrixCoroutineDispatchers: MatrixCoroutineDispatchers,
|
||||||
|
private val realmConfiguration: RealmConfiguration
|
||||||
|
) {
|
||||||
|
|
||||||
|
private lateinit var roomResults: RealmResults<RoomEntity>
|
||||||
|
private var isStarted = AtomicBoolean(false)
|
||||||
|
|
||||||
|
fun start() {
|
||||||
|
if (isStarted.compareAndSet(false, true)) {
|
||||||
|
roomResults = RoomEntity.getAllAsync(mainThreadRealm.instance)
|
||||||
|
roomResults.addChangeListener { rooms, changeSet ->
|
||||||
|
manageRoomResults(rooms, changeSet.changes)
|
||||||
|
manageRoomResults(rooms, changeSet.insertions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun dispose() {
|
||||||
|
if (isStarted.compareAndSet(true, false)) {
|
||||||
|
roomResults.removeAllChangeListeners()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// PRIVATE
|
||||||
|
|
||||||
|
private fun manageRoomResults(rooms: RealmResults<RoomEntity>, indexes: IntArray) {
|
||||||
|
indexes.forEach {
|
||||||
|
val room = rooms[it]
|
||||||
|
if (room != null) {
|
||||||
|
manageRoom(room.roomId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun manageRoom(roomId: String) = GlobalScope.launch(matrixCoroutineDispatchers.io) {
|
||||||
|
val realm = Realm.getInstance(realmConfiguration)
|
||||||
|
val roomEvents = EventEntity.getAllFromRoom(realm, roomId)
|
||||||
|
val lastNameEvent = roomEvents.getLast(EventType.STATE_ROOM_NAME)?.asDomain()
|
||||||
|
val lastTopicEvent = roomEvents.getLast(EventType.STATE_ROOM_TOPIC)?.asDomain()
|
||||||
|
val lastMessageEvent = roomEvents.getLast(EventType.MESSAGE)
|
||||||
|
|
||||||
|
realm.executeTransaction { realmInstance ->
|
||||||
|
val roomSummary = realmInstance.copyToRealmOrUpdate(RoomSummaryEntity(roomId))
|
||||||
|
roomSummary.displayName = lastNameEvent?.content<RoomNameContent>()?.name
|
||||||
|
roomSummary.topic = lastTopicEvent?.content<RoomTopicContent>()?.topic
|
||||||
|
roomSummary.lastMessage = lastMessageEvent
|
||||||
|
}
|
||||||
|
realm.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue