From 4904ac894e226139edb9b8405de6c83f4680d478 Mon Sep 17 00:00:00 2001 From: ganfra Date: Tue, 16 Oct 2018 15:52:30 +0200 Subject: [PATCH] Start to insert room summaries and listen for changes --- .idea/caches/build_file_checksums.ser | Bin 651 -> 651 bytes app/build.gradle | 2 + .../main/java/im/vector/riotredesign/Riot.kt | 5 ++ .../features/home/HomeActivity.kt | 22 +++-- .../features/login/LoginActivity.kt | 17 ++-- matrix-sdk-android/build.gradle | 5 ++ .../matrix/android/api/MatrixCallback.kt | 2 +- .../vector/matrix/android/api/events/Event.kt | 2 +- .../matrix/android/api/events/UnsignedData.kt | 4 +- .../android/api/rooms/RoomNameContent.kt | 9 ++ .../android/api/rooms/RoomTopicContent.kt | 9 ++ .../matrix/android/api/session/Session.kt | 9 +- .../internal/database/RealmInstanceHolder.kt | 11 --- .../internal/database/SessionRealmHolder.kt | 25 ++++++ .../internal/database/mapper/EventMapper.kt | 27 +++++- .../internal/database/model/EventEntity.kt | 7 +- .../internal/database/model/RoomEntity.kt | 6 +- .../database/model/RoomSummaryEntity.kt | 11 +++ .../database/query/ChunkEntityQueries.kt | 20 +++++ .../database/query/EventEntityQueries.kt | 19 +++++ .../database/query/RoomEntityQueries.kt | 7 +- .../android/internal/di/MatrixModule.kt | 1 - .../android/internal/di/NetworkModule.kt | 2 - .../android/internal/di/SessionModule.kt | 9 +- .../internal/events/sync/RoomSyncHandler.kt | 17 ++-- .../internal/events/sync/Synchronizer.kt | 3 + .../android/internal/network/Request.kt | 2 +- .../internal/session/DefaultSession.kt | 77 ++++++++++++------ .../internal/session/RoomSummaryObserver.kt | 73 +++++++++++++++++ 29 files changed, 319 insertions(+), 84 deletions(-) create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomNameContent.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomTopicContent.kt delete mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmInstanceHolder.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/SessionRealmHolder.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/RoomSummaryObserver.kt diff --git a/.idea/caches/build_file_checksums.ser b/.idea/caches/build_file_checksums.ser index 4a04b7f86f6b0e98b2256b0ced8b759fab4728db..3d334265dec011b5a457deca9220c0aa8634a839 100644 GIT binary patch delta 54 zcmV-60LlN01&alcm;`+J{R^?2EddZ5hgPFM8Y$3-=d6;?9cdwx*#V&uT7jQ2RqC(Z M)3#$b6%Pk~cy?JAKL7v# delta 54 zcmV-60LlN01&alcm;`t+64J4pEddbDpQ~sdEUVX^Zf4%dSC<5n*#V&uY`VYVks=3r M1@}3CczDJb7XSbN diff --git a/app/build.gradle b/app/build.gradle index bf121577e4..9712821c65 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,6 +28,8 @@ dependencies { implementation 'com.android.support:appcompat-v7:28.0.0' 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-scope:$koin_version" implementation "org.koin:koin-android-viewmodel:$koin_version" diff --git a/app/src/main/java/im/vector/riotredesign/Riot.kt b/app/src/main/java/im/vector/riotredesign/Riot.kt index 58b418f532..7a589a1237 100644 --- a/app/src/main/java/im/vector/riotredesign/Riot.kt +++ b/app/src/main/java/im/vector/riotredesign/Riot.kt @@ -1,13 +1,18 @@ package im.vector.riotredesign import android.app.Application +import im.vector.matrix.android.BuildConfig import im.vector.riotredesign.core.di.AppModule import org.koin.standalone.StandAloneContext.startKoin +import timber.log.Timber class Riot : Application() { override fun onCreate() { super.onCreate() + if (BuildConfig.DEBUG) { + Timber.plant(Timber.DebugTree()) + } startKoin(listOf(AppModule(this))) } diff --git a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt index 197b4d3a0c..56d391fe38 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/HomeActivity.kt @@ -4,24 +4,22 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.view.View -import android.widget.Toast import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.MatrixCallback 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.riotredesign.R import im.vector.riotredesign.core.platform.RiotActivity -import io.realm.RealmChangeListener -import io.realm.RealmResults import kotlinx.android.synthetic.main.activity_home.* import org.koin.android.ext.android.inject +import timber.log.Timber class HomeActivity : RiotActivity() { private val matrix by inject() private val synchronizer = matrix.currentSession?.synchronizer() - private val realmHolder = matrix.currentSession?.realmInstanceHolder() + private val realmHolder = matrix.currentSession?.realmHolder() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -33,25 +31,25 @@ class HomeActivity : RiotActivity() { synchronizeButton.visibility = View.GONE loadingView.visibility = View.VISIBLE synchronizer?.synchronize(object : MatrixCallback { - override fun onSuccess(data: SyncResponse?) { + override fun onSuccess(data: SyncResponse) { synchronizeButton.visibility = View.VISIBLE loadingView.visibility = View.GONE + Timber.v("Sync successful") } override fun onFailure(failure: Failure) { synchronizeButton.visibility = View.VISIBLE 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) { - val results = realmHolder.realm.where(EventEntity::class.java).equalTo("chunk.room.roomId", "!UlckfcnwgLKswCmUbe:matrix.org").findAll() - results.addChangeListener(RealmChangeListener> { - Toast.makeText(this@HomeActivity, "Room events data changed", Toast.LENGTH_LONG).show() - }) + val results = realmHolder.instance.where(RoomSummaryEntity::class.java).findAll() + results.addChangeListener { summaries -> + Timber.v("Summaries updated") + } } - } companion object { diff --git a/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt b/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt index dbe0898637..ab9a5977e4 100644 --- a/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt +++ b/app/src/main/java/im/vector/riotredesign/features/login/LoginActivity.kt @@ -22,8 +22,8 @@ class LoginActivity : RiotActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_login) - authenticateButton.setOnClickListener { authenticate() } checkActiveSessions() + authenticateButton.setOnClickListener { authenticate() } } private fun authenticate() { @@ -37,9 +37,8 @@ class LoginActivity : RiotActivity() { .build() authenticator.authenticate(homeServerConnectionConfig, login, password, object : MatrixCallback { - override fun onSuccess(data: Session?) { - matrix.currentSession = data - goToHomeScreen() + override fun onSuccess(data: Session) { + openSessionAndGoToHome(data) } override fun onFailure(failure: Failure) { @@ -51,12 +50,16 @@ class LoginActivity : RiotActivity() { private fun checkActiveSessions() { if (authenticator.hasActiveSessions()) { - matrix.currentSession = authenticator.getLastActiveSession() - goToHomeScreen() + val session = authenticator.getLastActiveSession() + session?.let { + openSessionAndGoToHome(it) + } } } - private fun goToHomeScreen() { + private fun openSessionAndGoToHome(session: Session) { + matrix.currentSession = session + session.open() val intent = HomeActivity.newIntent(this) startActivity(intent) finish() diff --git a/matrix-sdk-android/build.gradle b/matrix-sdk-android/build.gradle index c97968a9ef..905877d00c 100644 --- a/matrix-sdk-android/build.gradle +++ b/matrix-sdk-android/build.gradle @@ -44,6 +44,7 @@ dependencies { def arrow_version = "0.7.3" def support_version = '28.0.0' def moshi_version = '1.7.0' + def lifecycle_version = "1.1.1" implementation fileTree(dir: 'libs', include: ['*.aar']) 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:recyclerview-v7:$support_version" + implementation "android.arch.lifecycle:extensions:$lifecycle_version" + kapt "android.arch.lifecycle:compiler:$lifecycle_version" + + // Network implementation 'com.squareup.retrofit2:retrofit:2.4.0' implementation 'com.squareup.retrofit2:converter-moshi:2.4.0' diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt index a94446bc67..5106a4cb11 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/MatrixCallback.kt @@ -4,7 +4,7 @@ import im.vector.matrix.android.api.failure.Failure interface MatrixCallback { - fun onSuccess(data: T?) + fun onSuccess(data: T) fun onFailure(failure: Failure) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/Event.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/Event.kt index d715bd2406..7d46fed641 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/Event.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/Event.kt @@ -16,7 +16,7 @@ data class Event( @Json(name = "sender") val sender: String? = null, @Json(name = "state_key") val stateKey: 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 { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/UnsignedData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/UnsignedData.kt index 4bd9ac176d..f22604c3cb 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/UnsignedData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/events/UnsignedData.kt @@ -5,7 +5,7 @@ import com.squareup.moshi.JsonClass @JsonClass(generateAdapter = true) 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 = "transaction_id") val transactionId: String + @Json(name = "transaction_id") val transactionId: String? = null ) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomNameContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomNameContent.kt new file mode 100644 index 0000000000..bafa4149fb --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomNameContent.kt @@ -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 +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomTopicContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomTopicContent.kt new file mode 100644 index 0000000000..02ba7477b4 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/rooms/RoomTopicContent.kt @@ -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 +) \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt index 40b809b852..d1fa6bb900 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/Session.kt @@ -1,15 +1,20 @@ 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 interface Session { + @MainThread + fun open() + fun synchronizer(): Synchronizer // Visible for testing request directly. Will be deleted - fun realmInstanceHolder(): RealmInstanceHolder + fun realmHolder(): SessionRealmHolder + @MainThread fun close() } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmInstanceHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmInstanceHolder.kt deleted file mode 100644 index 7ba05ffc3c..0000000000 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/RealmInstanceHolder.kt +++ /dev/null @@ -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) - - -} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/SessionRealmHolder.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/SessionRealmHolder.kt new file mode 100644 index 0000000000..5d1195cb92 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/SessionRealmHolder.kt @@ -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) + } + + +} + diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt index 08e4bf121e..9ccc93074a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/mapper/EventMapper.kt @@ -2,6 +2,7 @@ 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.api.events.UnsignedData import im.vector.matrix.android.internal.database.model.EventEntity 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 adapter = moshi.adapter>(type) - fun map(event: Event): EventEntity { + internal 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 + eventEntity.sender = event.sender + eventEntity.originServerTs = event.originServerTs + eventEntity.age = event.unsignedData?.age ?: event.originServerTs 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) } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt index bc569b7770..750e30624d 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/EventEntity.kt @@ -9,9 +9,14 @@ open class EventEntity(@PrimaryKey var eventId: String = "", var type: String = "", var content: String = "", 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() { + companion object + @LinkingObjects("events") val chunk: RealmResults? = null diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt index 96b78d9c5d..74c6debdfe 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomEntity.kt @@ -6,10 +6,10 @@ import io.realm.annotations.Ignore import io.realm.annotations.PrimaryKey import kotlin.properties.Delegates -open class RoomEntity : RealmObject() { +open class RoomEntity(@PrimaryKey var roomId: String = "", + var chunks: RealmList = RealmList() +) : RealmObject() { - @PrimaryKey var roomId: String = "" - var chunks: RealmList = RealmList() private var membershipStr: String = Membership.NONE.name @delegate:Ignore var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt new file mode 100644 index 0000000000..c855be2b17 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/model/RoomSummaryEntity.kt @@ -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() \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt new file mode 100644 index 0000000000..c96ea6d291 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/ChunkEntityQueries.kt @@ -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): ChunkEntity? { + return realm.where(ChunkEntity::class.java) + .`in`("events.eventId", eventIds.toTypedArray()) + .findFirst() +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt new file mode 100644 index 0000000000..20cdd2b75d --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/EventEntityQueries.kt @@ -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 { + return realm.where(EventEntity::class.java) + .equalTo("chunk.room.roomId", roomId) + .findAll() +} + +fun RealmResults.getLast(type: String? = null): EventEntity? { + var query = this.where() + if (type != null) { + query = query.equalTo("type", type) + } + return query.findAll().sort("age").lastOrNull() +} \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomEntityQueries.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomEntityQueries.kt index 3858e22d93..6d5bacd810 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomEntityQueries.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/database/query/RoomEntityQueries.kt @@ -2,6 +2,7 @@ package im.vector.matrix.android.internal.database.query import im.vector.matrix.android.internal.database.model.RoomEntity import io.realm.Realm +import io.realm.RealmResults fun RoomEntity.Companion.getForId(realm: Realm, roomId: String): RoomEntity? { return realm.where(RoomEntity::class.java) @@ -9,10 +10,10 @@ fun RoomEntity.Companion.getForId(realm: Realm, roomId: String): RoomEntity? { .findFirst() } -fun RoomEntity.Companion.getAll(realm: Realm, membership: RoomEntity.Membership? = null): List { +fun RoomEntity.Companion.getAllAsync(realm: Realm, membership: RoomEntity.Membership? = null): RealmResults { val query = realm.where(RoomEntity::class.java) if (membership != null) { query.equalTo("membership", membership.name) } - return query.findAll() -} \ No newline at end of file + return query.findAllAsync() +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt index 50f6d7ac4f..7d4cfbf728 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/MatrixModule.kt @@ -3,7 +3,6 @@ package im.vector.matrix.android.internal.di import im.vector.matrix.android.api.MatrixOptions import im.vector.matrix.android.api.thread.MainThreadExecutor import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers -import io.realm.Realm import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.IO import kotlinx.coroutines.asCoroutineDispatcher diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt index 8fbe902e4f..9504b7b072 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/NetworkModule.kt @@ -1,9 +1,7 @@ package im.vector.matrix.android.internal.di 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.parsing.UriMoshiAdapter import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor import org.koin.dsl.context.ModuleDefinition diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/SessionModule.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/SessionModule.kt index 051bb0f921..66e653c064 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/SessionModule.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/di/SessionModule.kt @@ -1,11 +1,12 @@ package im.vector.matrix.android.internal.di 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.MXSession 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.RoomSummaryObserver import io.realm.RealmConfiguration import org.koin.dsl.context.ModuleDefinition import org.koin.dsl.module.Module @@ -21,7 +22,7 @@ class SessionModule(private val sessionParams: SessionParams) : Module { } scope(DefaultSession.SCOPE) { - RealmInstanceHolder(get()) + SessionRealmHolder(get()) } scope(DefaultSession.SCOPE) { @@ -31,6 +32,10 @@ class SessionModule(private val sessionParams: SessionParams) : Module { .build() } + scope(DefaultSession.SCOPE) { + RoomSummaryObserver(get(), get(), get()) + } + scope(DefaultSession.SCOPE) { val store = MXFileStore(sessionParams.credentials, false, get()) val dataHandler = MXDataHandler(store, sessionParams.credentials) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt index 7e166c3e1a..a96c73d32e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/RoomSyncHandler.kt @@ -1,10 +1,12 @@ package im.vector.matrix.android.internal.events.sync 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.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.getLastChunkFromRoom import im.vector.matrix.android.internal.events.sync.data.InvitedRoomSync import im.vector.matrix.android.internal.events.sync.data.RoomSync import io.realm.Realm @@ -39,8 +41,7 @@ class RoomSyncHandler(private val realmConfiguration: RealmConfiguration) { roomId: String, roomSync: RoomSync): RoomEntity { - val roomEntity = RoomEntity.getForId(realm, roomId) - ?: RoomEntity().apply { this.roomId = roomId } + val roomEntity = RoomEntity.getForId(realm, roomId) ?: RoomEntity(roomId) if (roomEntity.membership == RoomEntity.Membership.INVITED) { roomEntity.chunks.deleteAllFromRealm() @@ -95,16 +96,16 @@ class RoomSyncHandler(private val realmConfiguration: RealmConfiguration) { isLimited: Boolean = true): ChunkEntity { 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 { - realm.where(ChunkEntity::class.java).`in`("events.eventId", eventList.map { it.eventId }.toTypedArray()).findFirst() - } ?: ChunkEntity() + val eventIds = eventList.filter { it.eventId != null }.map { it.eventId!! } + ChunkEntity.getChunkIncludingEvents(realm, eventIds) + } ?: ChunkEntity().apply { this.prevToken = prevToken } - chunkEntity.prevToken = prevToken chunkEntity.nextToken = nextToken chunkEntity.isLimited = isLimited eventList.forEach { event -> - val eventEntity = EventMapper.map(event).let { + val eventEntity = event.asEntity().let { realm.copyToRealmOrUpdate(it) } if (!chunkEntity.events.contains(eventEntity)) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/Synchronizer.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/Synchronizer.kt index ef5d92a7b3..c834837002 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/Synchronizer.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/events/sync/Synchronizer.kt @@ -2,6 +2,7 @@ package im.vector.matrix.android.internal.events.sync import arrow.core.Either import arrow.core.flatMap +import arrow.core.leftIfNull import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.util.Cancelable @@ -42,6 +43,8 @@ class Synchronizer(private val syncAPI: SyncAPI, params["filter"] = filterBody.toJSONString() executeRequest { apiCall = syncAPI.sync(params) + }.leftIfNull { + Failure.Unknown(RuntimeException("Sync response shouln't be null")) }.flatMap { token = it?.nextBatch try { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt index 4184e2cf43..1777a7d39b 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/network/Request.kt @@ -32,7 +32,7 @@ class Request { } catch (e: Exception) { when (e) { is IOException -> Either.Left(Failure.NetworkConnection) - else -> Either.Left(Failure.Unknown(e)) + else -> Either.Left(Failure.Unknown(e)) } } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt index 70d73297b6..23e0bf5db6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/DefaultSession.kt @@ -1,8 +1,10 @@ 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.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.events.sync.SyncModule 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.inject -class DefaultSession(sessionParams: SessionParams) : Session, KoinComponent { - - private val realmInstanceHolder by inject() - private val synchronizer by inject() - 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() - } +class DefaultSession(private val sessionParams: SessionParams) : Session, KoinComponent { companion object { const val SCOPE: String = "session" } + private lateinit var scope: Scope + + private val realmInstanceHolder by inject() + private val synchronizer by inject() + private val roomSummaryObserver by inject() + + 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") + } + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/RoomSummaryObserver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/RoomSummaryObserver.kt new file mode 100644 index 0000000000..80777943dc --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/RoomSummaryObserver.kt @@ -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 + 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, 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()?.name + roomSummary.topic = lastTopicEvent?.content()?.topic + roomSummary.lastMessage = lastMessageEvent + } + realm.close() + } + +} \ No newline at end of file