Merge branch 'feature/Perf' into develop

This commit is contained in:
ganfra 2019-07-02 23:07:16 +02:00
commit bc2d321a84
30 changed files with 248 additions and 210 deletions

View File

@ -35,11 +35,11 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar']) implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation project(":matrix-sdk-android") implementation project(":matrix-sdk-android")
implementation 'androidx.appcompat:appcompat:1.1.0-alpha01' implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0' implementation 'io.reactivex.rxjava2:rxkotlin:2.3.0'
implementation 'io.reactivex.rxjava2:rxandroid:2.1.0' implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'


testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
} }

View File

@ -24,8 +24,8 @@ import io.reactivex.Observable


class RxRoom(private val room: Room) { class RxRoom(private val room: Room) {


fun liveRoomSummary(): Observable<RoomSummary> { fun liveRoomSummary(fetchLastEvent: Boolean): Observable<RoomSummary> {
return room.liveRoomSummary.asObservable() return room.liveRoomSummary(fetchLastEvent).asObservable()
} }


fun liveRoomMemberIds(): Observable<List<String>> { fun liveRoomMemberIds(): Observable<List<String>> {

View File

@ -25,8 +25,8 @@ import io.reactivex.Observable


class RxSession(private val session: Session) { class RxSession(private val session: Session) {


fun liveRoomSummaries(): Observable<List<RoomSummary>> { fun liveRoomSummaries(fetchLastEvents: Boolean): Observable<List<RoomSummary>> {
return session.liveRoomSummaries().asObservable() return session.liveRoomSummaries(fetchLastEvents).asObservable()
} }


fun liveGroupSummaries(): Observable<List<GroupSummary>> { fun liveGroupSummaries(): Observable<List<GroupSummary>> {

View File

@ -86,7 +86,7 @@ static def gitRevisionDate() {
dependencies { dependencies {


def arrow_version = "0.8.0" def arrow_version = "0.8.0"
def support_version = '1.1.0-alpha03' def support_version = '1.1.0-beta01'
def moshi_version = '1.8.0' def moshi_version = '1.8.0'
def lifecycle_version = '2.0.0' def lifecycle_version = '2.0.0'
def coroutines_version = "1.0.1" def coroutines_version = "1.0.1"
@ -98,8 +98,8 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"


implementation "androidx.appcompat:appcompat:$support_version" implementation "androidx.appcompat:appcompat:1.1.0-beta01"
implementation "androidx.recyclerview:recyclerview:$support_version" implementation "androidx.recyclerview:recyclerview:1.1.0-alpha06"


implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-extensions:$lifecycle_version"
kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version" kapt "androidx.lifecycle:lifecycle-compiler:$lifecycle_version"
@ -107,7 +107,7 @@ dependencies {
// 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'
implementation 'com.squareup.okhttp3:okhttp:3.11.0' implementation 'com.squareup.okhttp3:okhttp:3.14.1'
implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0' implementation 'com.squareup.okhttp3:logging-interceptor:3.10.0'
implementation 'com.novoda:merlin:1.1.6' implementation 'com.novoda:merlin:1.1.6'
implementation "com.squareup.moshi:moshi-adapters:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
@ -120,7 +120,7 @@ dependencies {
kapt 'dk.ilios:realmfieldnameshelper:1.1.1' kapt 'dk.ilios:realmfieldnameshelper:1.1.1'


// Work // Work
implementation "androidx.work:work-runtime-ktx:2.1.0-beta01" implementation "androidx.work:work-runtime-ktx:2.1.0-rc01"


// FP // FP
implementation "io.arrow-kt:arrow-core:$arrow_version" implementation "io.arrow-kt:arrow-core:$arrow_version"
@ -155,11 +155,11 @@ dependencies {
testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"


androidTestImplementation "org.koin:koin-test:$koin_version" androidTestImplementation "org.koin:koin-test:$koin_version"
androidTestImplementation 'androidx.test:core:1.1.0' androidTestImplementation 'androidx.test:core:1.2.0'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test:rules:1.1.1' androidTestImplementation 'androidx.test:rules:1.2.0'
androidTestImplementation 'androidx.test.ext:junit:1.1.0' androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
androidTestImplementation 'org.amshove.kluent:kluent-android:1.44' androidTestImplementation 'org.amshove.kluent:kluent-android:1.44'
androidTestImplementation "io.mockk:mockk-android:1.8.13.kotlin13" androidTestImplementation "io.mockk:mockk-android:1.8.13.kotlin13"
androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version" androidTestImplementation "androidx.arch.core:core-testing:$lifecycle_version"

View File

@ -47,8 +47,8 @@ interface Room :
* A live [RoomSummary] associated with the room * A live [RoomSummary] associated with the room
* You can observe this summary to get dynamic data from this room. * You can observe this summary to get dynamic data from this room.
*/ */
val liveRoomSummary: LiveData<RoomSummary> fun liveRoomSummary(fetchLastEvent: Boolean = false): LiveData<RoomSummary>


val roomSummary: RoomSummary? fun roomSummary(fetchLastEvent: Boolean = false): RoomSummary?


} }

View File

@ -43,6 +43,6 @@ interface RoomService {
* Get a live list of room summaries. This list is refreshed as soon as the data changes. * Get a live list of room summaries. This list is refreshed as soon as the data changes.
* @return the [LiveData] of [RoomSummary] * @return the [LiveData] of [RoomSummary]
*/ */
fun liveRoomSummaries(): LiveData<List<RoomSummary>> fun liveRoomSummaries(fetchLastEvents: Boolean = true): LiveData<List<RoomSummary>>


} }

View File

@ -25,6 +25,7 @@ import android.text.TextUtils
import arrow.core.Try import arrow.core.Try
import com.squareup.moshi.Types import com.squareup.moshi.Types
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import dagger.Lazy
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.failure.Failure import im.vector.matrix.android.api.failure.Failure
@ -98,7 +99,7 @@ internal class CryptoManager @Inject constructor(
private val olmManager: OlmManager, private val olmManager: OlmManager,
// The credentials, // The credentials,
private val credentials: Credentials, private val credentials: Credentials,
private val myDeviceInfoHolder: MyDeviceInfoHolder, private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
// the crypto store // the crypto store
private val cryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
// Olm device // Olm device
@ -190,7 +191,7 @@ internal class CryptoManager @Inject constructor(
} }


override fun getMyDevice(): MXDeviceInfo { override fun getMyDevice(): MXDeviceInfo {
return myDeviceInfoHolder.myDevice return myDeviceInfoHolder.get().myDevice
} }


override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) { override fun getDevicesList(callback: MatrixCallback<DevicesListResponse>) {

View File

@ -18,6 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification


import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import dagger.Lazy
import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.api.auth.data.Credentials import im.vector.matrix.android.api.auth.data.Credentials
import im.vector.matrix.android.api.session.crypto.sas.CancelCode import im.vector.matrix.android.api.session.crypto.sas.CancelCode
@ -55,7 +56,7 @@ import kotlin.collections.HashMap
@SessionScope @SessionScope
internal class DefaultSasVerificationService @Inject constructor(private val credentials: Credentials, internal class DefaultSasVerificationService @Inject constructor(private val credentials: Credentials,
private val cryptoStore: IMXCryptoStore, private val cryptoStore: IMXCryptoStore,
private val myDeviceInfoHolder: MyDeviceInfoHolder, private val myDeviceInfoHolder: Lazy<MyDeviceInfoHolder>,
private val deviceListManager: DeviceListManager, private val deviceListManager: DeviceListManager,
private val setDeviceVerificationAction: SetDeviceVerificationAction, private val setDeviceVerificationAction: SetDeviceVerificationAction,
private val sendToDeviceTask: SendToDeviceTask, private val sendToDeviceTask: SendToDeviceTask,
@ -197,7 +198,7 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
cryptoStore, cryptoStore,
sendToDeviceTask, sendToDeviceTask,
taskExecutor, taskExecutor,
myDeviceInfoHolder.myDevice.fingerprint()!!, myDeviceInfoHolder.get().myDevice.fingerprint()!!,
startReq.transactionID!!, startReq.transactionID!!,
otherUserId) otherUserId)
addTransaction(tx) addTransaction(tx)
@ -366,7 +367,7 @@ internal class DefaultSasVerificationService @Inject constructor(private val cre
cryptoStore, cryptoStore,
sendToDeviceTask, sendToDeviceTask,
taskExecutor, taskExecutor,
myDeviceInfoHolder.myDevice.fingerprint()!!, myDeviceInfoHolder.get().myDevice.fingerprint()!!,
txID, txID,
userId, userId,
deviceID) deviceID)

View File

@ -16,29 +16,24 @@


package im.vector.matrix.android.internal.database.mapper package im.vector.matrix.android.internal.database.mapper


import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.api.session.room.model.tag.RoomTag import im.vector.matrix.android.api.session.room.model.tag.RoomTag
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory import im.vector.matrix.android.internal.session.room.timeline.TimelineEventFactory
import javax.inject.Inject import javax.inject.Inject


internal class RoomSummaryMapper @Inject constructor(private val timelineEventFactory: TimelineEventFactory) {


internal class RoomSummaryMapper @Inject constructor( fun map(roomSummaryEntity: RoomSummaryEntity, getLatestEvent: Boolean = false): RoomSummary {
private val timelineEventFactory: TimelineEventFactory,
private val monarchy: Monarchy) {

fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
val tags = roomSummaryEntity.tags.map { val tags = roomSummaryEntity.tags.map {
RoomTag(it.tagName, it.tagOrder) RoomTag(it.tagName, it.tagOrder)
} }
val latestEvent = roomSummaryEntity.latestEvent?.let { val latestEvent = if (getLatestEvent) {
var ev: TimelineEvent? = null roomSummaryEntity.latestEvent?.let {
monarchy.doWithRealm { realm -> timelineEventFactory.create(it, it.realm)
ev = timelineEventFactory.create(it, realm)
} }
ev } else {
null
} }
return RoomSummary( return RoomSummary(
roomId = roomSummaryEntity.roomId, roomId = roomSummaryEntity.roomId,

View File

@ -48,58 +48,7 @@ internal class DefaultPushRuleService @Inject constructor(


override fun fetchPushRules(scope: String) { override fun fetchPushRules(scope: String) {
pushRulesTask pushRulesTask
.configureWith(Unit) .configureWith(GetPushRulesTask.Params(scope))
.dispatchTo(object : MatrixCallback<GetPushRulesResponse> {
override fun onSuccess(data: GetPushRulesResponse) {
monarchy.runTransactionSync { realm ->
//clear existings?
//TODO
realm.where(PushRulesEntity::class.java)
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
.findAll().deleteAllFromRealm()

val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content")
data.global.content?.forEach { rule ->
PushRulesMapper.map(rule).also {
content.pushRules.add(it)
}
}
realm.insertOrUpdate(content)

val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override")
data.global.override?.forEach { rule ->
PushRulesMapper.map(rule).also {
override.pushRules.add(it)
}
}
realm.insertOrUpdate(override)

val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room")
data.global.room?.forEach { rule ->
PushRulesMapper.map(rule).also {
rooms.pushRules.add(it)
}
}
realm.insertOrUpdate(rooms)

val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender")
data.global.sender?.forEach { rule ->
PushRulesMapper.map(rule).also {
senders.pushRules.add(it)
}
}
realm.insertOrUpdate(senders)

val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride")
data.global.underride?.forEach { rule ->
PushRulesMapper.map(rule).also {
underrides.pushRules.add(it)
}
}
realm.insertOrUpdate(underrides)
}
}
})
.executeBy(taskExecutor) .executeBy(taskExecutor)
} }



View File

@ -54,22 +54,6 @@ internal class DefaultPusherService @Inject constructor(
override fun refreshPushers() { override fun refreshPushers() {
getPusherTask getPusherTask
.configureWith(Unit) .configureWith(Unit)
.dispatchTo(object : MatrixCallback<GetPushersResponse> {
override fun onSuccess(data: GetPushersResponse) {
monarchy.runTransactionSync { realm ->
//clear existings?
realm.where(PusherEntity::class.java)
.equalTo(PusherEntityFields.USER_ID, sessionParam.credentials.userId)
.findAll().deleteAllFromRealm()
data.pushers?.forEach { jsonPusher ->
jsonPusher.toEntity(sessionParam.credentials.userId).also {
it.state = PusherState.REGISTERED
realm.insertOrUpdate(it)
}
}
}
}
})
.executeBy(taskExecutor) .executeBy(taskExecutor)
} }



View File

@ -16,19 +16,81 @@
package im.vector.matrix.android.internal.session.pushers package im.vector.matrix.android.internal.session.pushers


import arrow.core.Try import arrow.core.Try
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse import im.vector.matrix.android.api.pushrules.rest.GetPushRulesResponse
import im.vector.matrix.android.internal.database.mapper.PushRulesMapper
import im.vector.matrix.android.internal.database.model.PushRulesEntity
import im.vector.matrix.android.internal.database.model.PusherEntityFields
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.tryTransactionSync
import javax.inject.Inject import javax.inject.Inject




internal interface GetPushRulesTask : Task<Unit, GetPushRulesResponse> internal interface GetPushRulesTask : Task<GetPushRulesTask.Params, Unit> {


internal class DefaultGetPushRulesTask @Inject constructor(private val pushRulesApi: PushRulesApi) : GetPushRulesTask { data class Params(val scope: String)


override suspend fun execute(params: Unit): Try<GetPushRulesResponse> { }
return executeRequest {

internal class DefaultGetPushRulesTask @Inject constructor(private val pushRulesApi: PushRulesApi,
private val monarchy: Monarchy,
private val sessionParams: SessionParams) : GetPushRulesTask {

override suspend fun execute(params: GetPushRulesTask.Params): Try<Unit> {
return executeRequest<GetPushRulesResponse> {
apiCall = pushRulesApi.getAllRules() apiCall = pushRulesApi.getAllRules()
}.flatMap { response ->
val scope = params.scope
return monarchy.tryTransactionSync { realm ->
//clear existings?
//TODO
realm.where(PushRulesEntity::class.java)
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
.findAll().deleteAllFromRealm()

val content = PushRulesEntity(sessionParams.credentials.userId, scope, "content")
response.global.content?.forEach { rule ->
PushRulesMapper.map(rule).also {
content.pushRules.add(it)
}
}
realm.insertOrUpdate(content)

val override = PushRulesEntity(sessionParams.credentials.userId, scope, "override")
response.global.override?.forEach { rule ->
PushRulesMapper.map(rule).also {
override.pushRules.add(it)
}
}
realm.insertOrUpdate(override)

val rooms = PushRulesEntity(sessionParams.credentials.userId, scope, "room")
response.global.room?.forEach { rule ->
PushRulesMapper.map(rule).also {
rooms.pushRules.add(it)
}
}
realm.insertOrUpdate(rooms)

val senders = PushRulesEntity(sessionParams.credentials.userId, scope, "sender")
response.global.sender?.forEach { rule ->
PushRulesMapper.map(rule).also {
senders.pushRules.add(it)
}
}
realm.insertOrUpdate(senders)

val underrides = PushRulesEntity(sessionParams.credentials.userId, scope, "underride")
response.global.underride?.forEach { rule ->
PushRulesMapper.map(rule).also {
underrides.pushRules.add(it)
}
}
realm.insertOrUpdate(underrides)
}
} }
} }
} }

View File

@ -16,17 +16,39 @@
package im.vector.matrix.android.internal.session.pushers package im.vector.matrix.android.internal.session.pushers


import arrow.core.Try import arrow.core.Try
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.auth.data.SessionParams
import im.vector.matrix.android.api.session.pushers.PusherState
import im.vector.matrix.android.internal.database.mapper.toEntity
import im.vector.matrix.android.internal.database.model.PusherEntity
import im.vector.matrix.android.internal.database.model.PusherEntityFields
import im.vector.matrix.android.internal.network.executeRequest import im.vector.matrix.android.internal.network.executeRequest
import im.vector.matrix.android.internal.task.Task import im.vector.matrix.android.internal.task.Task
import im.vector.matrix.android.internal.util.tryTransactionSync
import javax.inject.Inject import javax.inject.Inject


internal interface GetPushersTask : Task<Unit, GetPushersResponse> internal interface GetPushersTask : Task<Unit, Unit>


internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: PushersAPI) : GetPushersTask { internal class DefaultGetPusherTask @Inject constructor(private val pushersAPI: PushersAPI,
private val monarchy: Monarchy,
private val sessionParams: SessionParams) : GetPushersTask {


override suspend fun execute(params: Unit): Try<GetPushersResponse> { override suspend fun execute(params: Unit): Try<Unit> {
return executeRequest { return executeRequest<GetPushersResponse> {
apiCall = pushersAPI.getPushers() apiCall = pushersAPI.getPushers()
}.flatMap { response ->
monarchy.tryTransactionSync { realm ->
//clear existings?
realm.where(PusherEntity::class.java)
.equalTo(PusherEntityFields.USER_ID, sessionParams.credentials.userId)
.findAll().deleteAllFromRealm()
response.pushers?.forEach { jsonPusher ->
jsonPusher.toEntity(sessionParams.credentials.userId).also {
it.state = PusherState.REGISTERED
realm.insertOrUpdate(it)
}
}
}
} }
} }
} }

View File

@ -34,6 +34,7 @@ import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.util.fetchCopied import im.vector.matrix.android.internal.util.fetchCopied
import im.vector.matrix.android.internal.util.fetchCopyMap
import javax.inject.Inject import javax.inject.Inject


internal class DefaultRoom @Inject constructor(override val roomId: String, internal class DefaultRoom @Inject constructor(override val roomId: String,
@ -54,12 +55,12 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
RelationService by relationService, RelationService by relationService,
MembershipService by roomMembersService { MembershipService by roomMembersService {


override val liveRoomSummary: LiveData<RoomSummary> by lazy { override fun liveRoomSummary(fetchLastEvent: Boolean): LiveData<RoomSummary> {
val liveRealmData = RealmLiveData<RoomSummaryEntity>(monarchy.realmConfiguration) { realm -> val liveRealmData = RealmLiveData<RoomSummaryEntity>(monarchy.realmConfiguration) { realm ->
RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) RoomSummaryEntity.where(realm, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME)
} }
Transformations.map(liveRealmData) { results -> return Transformations.map(liveRealmData) { results ->
val roomSummaries = results.map { roomSummaryMapper.map(it) } val roomSummaries = results.map { roomSummaryMapper.map(it, fetchLastEvent) }


if (roomSummaries.isEmpty()) { if (roomSummaries.isEmpty()) {
// Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache // Create a dummy RoomSummary to avoid Crash during Sign Out or clear cache
@ -70,10 +71,11 @@ internal class DefaultRoom @Inject constructor(override val roomId: String,
} }
} }


override val roomSummary: RoomSummary? override fun roomSummary(fetchLastEvent: Boolean): RoomSummary? {
get() { return monarchy.fetchAllMappedSync(
var sum: RoomSummaryEntity? = monarchy.fetchCopied { RoomSummaryEntity.where(it, roomId).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME).findFirst() } { realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
return sum?.let { roomSummaryMapper.map(it) } { roomSummaryMapper.map(it, fetchLastEvent) }
).firstOrNull()
} }


override fun isEncrypted(): Boolean { override fun isEncrypted(): Boolean {

View File

@ -52,10 +52,10 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona
return roomFactory.create(roomId) return roomFactory.create(roomId)
} }


override fun liveRoomSummaries(): LiveData<List<RoomSummary>> { override fun liveRoomSummaries(fetchLastEvents: Boolean): LiveData<List<RoomSummary>> {
return monarchy.findAllMappedWithChanges( return monarchy.findAllMappedWithChanges(
{ realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) }, { realm -> RoomSummaryEntity.where(realm).isNotEmpty(RoomSummaryEntityFields.DISPLAY_NAME) },
{ roomSummaryMapper.map(it) } { roomSummaryMapper.map(it, fetchLastEvents) }
) )
} }
} }

View File

@ -30,7 +30,12 @@ import im.vector.matrix.android.api.util.addTo
import im.vector.matrix.android.internal.crypto.NewSessionListener import im.vector.matrix.android.internal.crypto.NewSessionListener
import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent import im.vector.matrix.android.internal.crypto.model.event.EncryptedEventContent
import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
import im.vector.matrix.android.internal.database.model.* import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.ChunkEntityFields
import im.vector.matrix.android.internal.database.model.EventAnnotationsSummaryEntity
import im.vector.matrix.android.internal.database.model.EventEntity
import im.vector.matrix.android.internal.database.model.EventEntityFields
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.database.query.findIncludingEvent import im.vector.matrix.android.internal.database.query.findIncludingEvent
import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom import im.vector.matrix.android.internal.database.query.findLastLiveChunkFromRoom
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
@ -38,7 +43,13 @@ import im.vector.matrix.android.internal.database.query.whereInRoom
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import im.vector.matrix.android.internal.util.Debouncer import im.vector.matrix.android.internal.util.Debouncer
import io.realm.* import io.realm.OrderedCollectionChangeSet
import io.realm.OrderedRealmCollectionChangeListener
import io.realm.Realm
import io.realm.RealmConfiguration
import io.realm.RealmQuery
import io.realm.RealmResults
import io.realm.Sort
import timber.log.Timber import timber.log.Timber
import java.util.* import java.util.*
import java.util.concurrent.atomic.AtomicBoolean import java.util.concurrent.atomic.AtomicBoolean
@ -50,7 +61,6 @@ import kotlin.collections.HashMap
private const val INITIAL_LOAD_SIZE = 20 private const val INITIAL_LOAD_SIZE = 20
private const val MIN_FETCHING_COUNT = 30 private const val MIN_FETCHING_COUNT = 30
private const val DISPLAY_INDEX_UNKNOWN = Int.MIN_VALUE private const val DISPLAY_INDEX_UNKNOWN = Int.MIN_VALUE
private const val THREAD_NAME = "TIMELINE_DB_THREAD"


internal class DefaultTimeline( internal class DefaultTimeline(
private val roomId: String, private val roomId: String,
@ -64,18 +74,22 @@ internal class DefaultTimeline(
private val allowedTypes: List<String>? private val allowedTypes: List<String>?
) : Timeline { ) : Timeline {


private companion object {
val BACKGROUND_HANDLER = Handler(
HandlerThread("TIMELINE_DB_THREAD").apply { start() }.looper
)
}

override var listener: Timeline.Listener? = null override var listener: Timeline.Listener? = null
set(value) { set(value) {
field = value field = value
backgroundHandler.get()?.post { BACKGROUND_HANDLER.post {
postSnapshot() postSnapshot()
} }
} }


private val isStarted = AtomicBoolean(false) private val isStarted = AtomicBoolean(false)
private val isReady = AtomicBoolean(false) private val isReady = AtomicBoolean(false)
private val backgroundHandlerThread = AtomicReference<HandlerThread>()
private val backgroundHandler = AtomicReference<Handler>()
private val mainHandler = Handler(Looper.getMainLooper()) private val mainHandler = Handler(Looper.getMainLooper())
private val backgroundRealm = AtomicReference<Realm>() private val backgroundRealm = AtomicReference<Realm>()
private val cancelableBag = CancelableBag() private val cancelableBag = CancelableBag()
@ -168,7 +182,7 @@ internal class DefaultTimeline(
override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) { override fun onNewSession(roomId: String?, senderKey: String, sessionId: String) {
if (roomId == this@DefaultTimeline.roomId) { if (roomId == this@DefaultTimeline.roomId) {
Timber.v("New session id detected for this room") Timber.v("New session id detected for this room")
backgroundHandler.get()?.post { BACKGROUND_HANDLER.post {
val realm = backgroundRealm.get() val realm = backgroundRealm.get()
var hasChange = false var hasChange = false
builtEvents.forEachIndexed { index, timelineEvent -> builtEvents.forEachIndexed { index, timelineEvent ->
@ -194,7 +208,7 @@ internal class DefaultTimeline(
// Public methods ****************************************************************************** // Public methods ******************************************************************************


override fun paginate(direction: Timeline.Direction, count: Int) { override fun paginate(direction: Timeline.Direction, count: Int) {
backgroundHandler.get()?.post { BACKGROUND_HANDLER.post {
if (!canPaginate(direction)) { if (!canPaginate(direction)) {
return@post return@post
} }
@ -211,13 +225,8 @@ internal class DefaultTimeline(
override fun start() { override fun start() {
if (isStarted.compareAndSet(false, true)) { if (isStarted.compareAndSet(false, true)) {
Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId") Timber.v("Start timeline for roomId: $roomId and eventId: $initialEventId")
val handlerThread = HandlerThread(THREAD_NAME + hashCode())
handlerThread.start()
val handler = Handler(handlerThread.looper)
this.backgroundHandlerThread.set(handlerThread)
this.backgroundHandler.set(handler)
cryptoService.addNewSessionListener(newSessionListener) cryptoService.addNewSessionListener(newSessionListener)
handler.post { BACKGROUND_HANDLER.post {
val realm = Realm.getInstance(realmConfiguration) val realm = Realm.getInstance(realmConfiguration)
backgroundRealm.set(realm) backgroundRealm.set(realm)
clearUnlinkedEvents(realm) clearUnlinkedEvents(realm)
@ -246,14 +255,12 @@ internal class DefaultTimeline(
if (isStarted.compareAndSet(true, false)) { if (isStarted.compareAndSet(true, false)) {
cryptoService.removeSessionListener(newSessionListener) cryptoService.removeSessionListener(newSessionListener)
Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId") Timber.v("Dispose timeline for roomId: $roomId and eventId: $initialEventId")
backgroundHandler.get()?.post { BACKGROUND_HANDLER.post {
cancelableBag.cancel() cancelableBag.cancel()
liveEvents.removeAllChangeListeners() liveEvents.removeAllChangeListeners()
backgroundRealm.getAndSet(null).also { backgroundRealm.getAndSet(null).also {
it.close() it.close()
} }
backgroundHandler.set(null)
backgroundHandlerThread.getAndSet(null)?.quit()
} }
} }
} }
@ -400,7 +407,7 @@ internal class DefaultTimeline(
Timber.v("Success fetching $limit items $direction from pagination request") Timber.v("Success fetching $limit items $direction from pagination request")
} else { } else {
// Database won't be updated, so we force pagination request // Database won't be updated, so we force pagination request
backgroundHandler.get()?.post { BACKGROUND_HANDLER.post {
executePaginationTask(direction, limit) executePaginationTask(direction, limit)
} }
} }
@ -441,6 +448,7 @@ internal class DefaultTimeline(
if (count < 1) { if (count < 1) {
return 0 return 0
} }
val start = System.currentTimeMillis()
val offsetResults = getOffsetResults(startDisplayIndex, direction, count) val offsetResults = getOffsetResults(startDisplayIndex, direction, count)
if (offsetResults.isEmpty()) { if (offsetResults.isEmpty()) {
return 0 return 0
@ -459,7 +467,8 @@ internal class DefaultTimeline(
builtEventsIdMap.entries.filter { it.value >= position }.forEach { it.setValue(it.value + 1) } builtEventsIdMap.entries.filter { it.value >= position }.forEach { it.setValue(it.value + 1) }
builtEventsIdMap[eventEntity.eventId] = position builtEventsIdMap[eventEntity.eventId] = position
} }
Timber.v("Built ${offsetResults.size} items from db") val time = System.currentTimeMillis() - start
Timber.v("Built ${offsetResults.size} items from db in $time ms")
return offsetResults.size return offsetResults.size
} }



View File

@ -80,7 +80,7 @@ internal class SimpleTimelineEventFactory @Inject constructor(private val roomMe
val result = cryptoService.decryptEvent(event, UUID.randomUUID().toString()) val result = cryptoService.decryptEvent(event, UUID.randomUUID().toString())
event.setClearData(result) event.setClearData(result)
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.e(failure, "Encrypted event: decryption failed") Timber.e("Encrypted event: decryption failed")
if (failure is MXDecryptionException) { if (failure is MXDecryptionException) {
event.setCryptoError(failure.cryptoError) event.setCryptoError(failure.cryptoError)
} }

View File

@ -156,9 +156,10 @@ dependencies {
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"


implementation 'androidx.appcompat:appcompat:1.1.0-alpha03' implementation 'androidx.appcompat:appcompat:1.1.0-beta01'
//Do not use beta2 at the moment, as it breaks things
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1' implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta1'
implementation 'androidx.core:core-ktx:1.0.1' implementation 'androidx.core:core-ktx:1.0.2'


implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1' implementation 'com.jakewharton.threetenabp:threetenabp:1.1.1'
implementation "com.squareup.moshi:moshi-adapters:$moshi_version" implementation "com.squareup.moshi:moshi-adapters:$moshi_version"
@ -185,7 +186,7 @@ dependencies {
implementation 'com.airbnb.android:mvrx:1.0.1' implementation 'com.airbnb.android:mvrx:1.0.1'


// Work // Work
implementation "androidx.work:work-runtime-ktx:2.1.0-beta01" implementation "androidx.work:work-runtime-ktx:2.1.0-rc01"


// Functional Programming // Functional Programming
implementation "io.arrow-kt:arrow-core:$arrow_version" implementation "io.arrow-kt:arrow-core:$arrow_version"
@ -195,7 +196,7 @@ dependencies {


// UI // UI
implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1' implementation 'com.amulyakhare:com.amulyakhare.textdrawable:1.0.1'
implementation 'com.google.android.material:material:1.1.0-alpha04' implementation 'com.google.android.material:material:1.1.0-alpha07'
implementation 'me.gujun.android:span:1.7' implementation 'me.gujun.android:span:1.7'
implementation "ru.noties.markwon:core:$markwon_version" implementation "ru.noties.markwon:core:$markwon_version"
implementation "ru.noties.markwon:html:$markwon_version" implementation "ru.noties.markwon:html:$markwon_version"
@ -246,8 +247,8 @@ dependencies {


// TESTS // TESTS
testImplementation 'junit:junit:4.12' testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test:runner:1.1.1' androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1' androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
} }


if (!getGradle().getStartParameter().getTaskRequests().toString().contains("Fdroid")) { if (!getGradle().getStartParameter().getTaskRequests().toString().contains("Fdroid")) {

View File

@ -33,6 +33,7 @@ import im.vector.riotx.features.home.AvatarRenderer
import im.vector.riotx.features.home.HomeNavigator import im.vector.riotx.features.home.HomeNavigator
import im.vector.riotx.features.home.HomeRoomListObservableStore import im.vector.riotx.features.home.HomeRoomListObservableStore
import im.vector.riotx.features.home.group.SelectedGroupStore import im.vector.riotx.features.home.group.SelectedGroupStore
import im.vector.riotx.features.html.EventHtmlRenderer
import im.vector.riotx.features.navigation.Navigator import im.vector.riotx.features.navigation.Navigator
import im.vector.riotx.features.notifications.NotifiableEventResolver import im.vector.riotx.features.notifications.NotifiableEventResolver
import im.vector.riotx.features.notifications.NotificationBroadcastReceiver import im.vector.riotx.features.notifications.NotificationBroadcastReceiver
@ -68,6 +69,8 @@ interface VectorComponent {


fun emojiCompatFontProvider(): EmojiCompatFontProvider fun emojiCompatFontProvider(): EmojiCompatFontProvider


fun eventHtmlRenderer(): EventHtmlRenderer

fun navigator(): Navigator fun navigator(): Navigator


fun homeNavigator(): HomeNavigator fun homeNavigator(): HomeNavigator

View File

@ -73,7 +73,7 @@ class HomeActivityViewModel @AssistedInject constructor(@Assisted initialState:
private fun observeRoomAndGroup() { private fun observeRoomAndGroup() {
Observable Observable
.combineLatest<List<RoomSummary>, Option<GroupSummary>, List<RoomSummary>>( .combineLatest<List<RoomSummary>, Option<GroupSummary>, List<RoomSummary>>(
session.rx().liveRoomSummaries().throttleLast(300, TimeUnit.MILLISECONDS), session.rx().liveRoomSummaries(fetchLastEvents = true).throttleLast(300, TimeUnit.MILLISECONDS),
selectedGroupStore.observe(), selectedGroupStore.observe(),
BiFunction { rooms, selectedGroupOption -> BiFunction { rooms, selectedGroupOption ->
val selectedGroup = selectedGroupOption.orNull() val selectedGroup = selectedGroupOption.orNull()

View File

@ -498,7 +498,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro
} }


private fun observeRoomSummary() { private fun observeRoomSummary() {
room.rx().liveRoomSummary() room.rx().liveRoomSummary(false)
.execute { async -> .execute { async ->
copy( copy(
asyncRoomSummary = async, asyncRoomSummary = async,

View File

@ -18,6 +18,7 @@ package im.vector.riotx.features.home.room.detail.timeline.action
import com.airbnb.mvrx.* import com.airbnb.mvrx.*
import com.squareup.inject.assisted.Assisted import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject import com.squareup.inject.assisted.AssistedInject
import dagger.Lazy
import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.Session
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
@ -84,7 +85,7 @@ data class MessageActionState(
*/ */
class MessageActionsViewModel @AssistedInject constructor(@Assisted class MessageActionsViewModel @AssistedInject constructor(@Assisted
initialState: MessageActionState, initialState: MessageActionState,
private val eventHtmlRenderer: EventHtmlRenderer, private val eventHtmlRenderer: Lazy<EventHtmlRenderer>,
session: Session, session: Session,
private val noticeEventFormatter: NoticeEventFormatter private val noticeEventFormatter: NoticeEventFormatter
) : VectorViewModel<MessageActionState>(initialState) { ) : VectorViewModel<MessageActionState>(initialState) {
@ -121,7 +122,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
} }


fun resolveBody(state: MessageActionState): CharSequence? { fun resolveBody(state: MessageActionState): CharSequence? {
return state.messageBody(eventHtmlRenderer, noticeEventFormatter) return state.messageBody(eventHtmlRenderer.get(), noticeEventFormatter)
} }


} }

View File

@ -23,12 +23,20 @@ import android.text.style.ClickableSpan
import android.text.style.ForegroundColorSpan import android.text.style.ForegroundColorSpan
import android.text.style.RelativeSizeSpan import android.text.style.RelativeSizeSpan
import android.view.View import android.view.View
import dagger.Lazy
import im.vector.matrix.android.api.permalinks.MatrixLinkify import im.vector.matrix.android.api.permalinks.MatrixLinkify
import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan import im.vector.matrix.android.api.permalinks.MatrixPermalinkSpan
import im.vector.matrix.android.api.session.events.model.RelationType import im.vector.matrix.android.api.session.events.model.RelationType
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary import im.vector.matrix.android.api.session.room.model.EditAggregatedSummary
import im.vector.matrix.android.api.session.room.model.message.* import im.vector.matrix.android.api.session.room.model.message.MessageAudioContent
import im.vector.matrix.android.api.session.room.model.message.MessageContent
import im.vector.matrix.android.api.session.room.model.message.MessageEmoteContent
import im.vector.matrix.android.api.session.room.model.message.MessageFileContent
import im.vector.matrix.android.api.session.room.model.message.MessageImageContent
import im.vector.matrix.android.api.session.room.model.message.MessageNoticeContent
import im.vector.matrix.android.api.session.room.model.message.MessageTextContent
import im.vector.matrix.android.api.session.room.model.message.MessageVideoContent
import im.vector.matrix.android.api.session.room.send.SendState import im.vector.matrix.android.api.session.room.send.SendState
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt import im.vector.matrix.android.internal.crypto.attachments.toElementToDecrypt
@ -55,7 +63,7 @@ class MessageItemFactory @Inject constructor(
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val colorProvider: ColorProvider, private val colorProvider: ColorProvider,
private val timelineMediaSizeProvider: TimelineMediaSizeProvider, private val timelineMediaSizeProvider: TimelineMediaSizeProvider,
private val htmlRenderer: EventHtmlRenderer, private val htmlRenderer: Lazy<EventHtmlRenderer>,
private val stringProvider: StringProvider, private val stringProvider: StringProvider,
private val emojiCompatFontProvider: EmojiCompatFontProvider, private val emojiCompatFontProvider: EmojiCompatFontProvider,
private val imageContentRenderer: ImageContentRenderer, private val imageContentRenderer: ImageContentRenderer,
@ -266,7 +274,7 @@ class MessageItemFactory @Inject constructor(
callback: TimelineEventController.Callback?): MessageTextItem? { callback: TimelineEventController.Callback?): MessageTextItem? {


val bodyToUse = messageContent.formattedBody?.let { val bodyToUse = messageContent.formattedBody?.let {
htmlRenderer.render(it.trim()) htmlRenderer.get().render(it.trim())
} ?: messageContent.body } ?: messageContent.body


val linkifiedBody = linkifyBody(bodyToUse, callback) val linkifiedBody = linkifyBody(bodyToUse, callback)

View File

@ -18,10 +18,9 @@ package im.vector.riotx.features.html


import android.content.Context import android.content.Context
import android.text.style.URLSpan import android.text.style.URLSpan
import androidx.appcompat.app.AppCompatActivity
import im.vector.matrix.android.api.permalinks.PermalinkData import im.vector.matrix.android.api.permalinks.PermalinkData
import im.vector.matrix.android.api.permalinks.PermalinkParser import im.vector.matrix.android.api.permalinks.PermalinkParser
import im.vector.matrix.android.api.session.Session import im.vector.riotx.core.di.ActiveSessionHolder
import im.vector.riotx.core.glide.GlideApp import im.vector.riotx.core.glide.GlideApp
import im.vector.riotx.core.glide.GlideRequests import im.vector.riotx.core.glide.GlideRequests
import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.AvatarRenderer
@ -37,12 +36,14 @@ import ru.noties.markwon.html.TagHandler
import ru.noties.markwon.html.tag.* import ru.noties.markwon.html.tag.*
import java.util.Arrays.asList import java.util.Arrays.asList
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton


class EventHtmlRenderer @Inject constructor(context: AppCompatActivity, @Singleton
class EventHtmlRenderer @Inject constructor(context: Context,
val avatarRenderer: AvatarRenderer, val avatarRenderer: AvatarRenderer,
session: Session) { sessionHolder: ActiveSessionHolder) {
private val markwon = Markwon.builder(context) private val markwon = Markwon.builder(context)
.usePlugin(MatrixPlugin.create(GlideApp.with(context), context, avatarRenderer, session)) .usePlugin(MatrixPlugin.create(GlideApp.with(context), context, avatarRenderer, sessionHolder))
.build() .build()


fun render(text: String): CharSequence { fun render(text: String): CharSequence {
@ -54,7 +55,7 @@ class EventHtmlRenderer @Inject constructor(context: AppCompatActivity,
private class MatrixPlugin private constructor(private val glideRequests: GlideRequests, private class MatrixPlugin private constructor(private val glideRequests: GlideRequests,
private val context: Context, private val context: Context,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val session: Session) : AbstractMarkwonPlugin() { private val session: ActiveSessionHolder) : AbstractMarkwonPlugin() {


override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { override fun configureConfiguration(builder: MarkwonConfiguration.Builder) {
builder.htmlParser(MarkwonHtmlParserImpl.create()) builder.htmlParser(MarkwonHtmlParserImpl.create())
@ -122,7 +123,7 @@ private class MatrixPlugin private constructor(private val glideRequests: GlideR


companion object { companion object {


fun create(glideRequests: GlideRequests, context: Context, avatarRenderer: AvatarRenderer, session: Session): MatrixPlugin { fun create(glideRequests: GlideRequests, context: Context, avatarRenderer: AvatarRenderer, session: ActiveSessionHolder): MatrixPlugin {
return MatrixPlugin(glideRequests, context, avatarRenderer, session) return MatrixPlugin(glideRequests, context, avatarRenderer, session)
} }
} }
@ -131,7 +132,7 @@ private class MatrixPlugin private constructor(private val glideRequests: GlideR
private class MxLinkHandler(private val glideRequests: GlideRequests, private class MxLinkHandler(private val glideRequests: GlideRequests,
private val context: Context, private val context: Context,
private val avatarRenderer: AvatarRenderer, private val avatarRenderer: AvatarRenderer,
private val session: Session) : TagHandler() { private val sessionHolder: ActiveSessionHolder) : TagHandler() {


private val linkHandler = LinkHandler() private val linkHandler = LinkHandler()


@ -141,7 +142,7 @@ private class MxLinkHandler(private val glideRequests: GlideRequests,
val permalinkData = PermalinkParser.parse(link) val permalinkData = PermalinkParser.parse(link)
when (permalinkData) { when (permalinkData) {
is PermalinkData.UserLink -> { is PermalinkData.UserLink -> {
val user = session.getUser(permalinkData.userId) val user = sessionHolder.getSafeActiveSession()?.getUser(permalinkData.userId)
val span = PillImageSpan(glideRequests, avatarRenderer, context, permalinkData.userId, user) val span = PillImageSpan(glideRequests, avatarRenderer, context, permalinkData.userId, user)
SpannableBuilder.setSpans( SpannableBuilder.setSpans(
visitor.builder(), visitor.builder(),

View File

@ -117,7 +117,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
val body = event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body val body = event.annotations?.editSummary?.aggregatedContent?.toModel<MessageContent>()?.body
?: event.root.getClearContent().toModel<MessageContent>()?.body ?: event.root.getClearContent().toModel<MessageContent>()?.body
?: stringProvider.getString(R.string.notification_unknown_new_event) ?: stringProvider.getString(R.string.notification_unknown_new_event)
val roomName = room.roomSummary?.displayName ?: "" val roomName = room.roomSummary()?.displayName ?: ""
val senderDisplayName = event.senderName ?: event.root.senderId val senderDisplayName = event.senderName ?: event.root.senderId


val notifiableEvent = NotifiableMessageEvent( val notifiableEvent = NotifiableMessageEvent(
@ -129,7 +129,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
body = body, body = body,
roomId = event.root.roomId!!, roomId = event.root.roomId!!,
roomName = roomName, roomName = roomName,
roomIsDirect = room.roomSummary?.isDirect ?: false) roomIsDirect = room.roomSummary()?.isDirect ?: false)


notifiableEvent.matrixID = session.sessionParams.credentials.userId notifiableEvent.matrixID = session.sessionParams.credentials.userId
notifiableEvent.soundName = null notifiableEvent.soundName = null
@ -137,7 +137,7 @@ class NotifiableEventResolver @Inject constructor(private val stringProvider: St
// Get the avatars URL // Get the avatars URL
// TODO They will be not displayed the first time (known limitation) // TODO They will be not displayed the first time (known limitation)
notifiableEvent.roomAvatarPath = session.contentUrlResolver() notifiableEvent.roomAvatarPath = session.contentUrlResolver()
.resolveThumbnail(room.roomSummary?.avatarUrl, .resolveThumbnail(room.roomSummary()?.avatarUrl,
250, 250,
250, 250,
ContentUrlResolver.ThumbnailMethod.SCALE) ContentUrlResolver.ThumbnailMethod.SCALE)

View File

@ -100,8 +100,8 @@ class NotificationBroadcastReceiver : BroadcastReceiver() {
session.sessionParams.credentials.userId, session.sessionParams.credentials.userId,
message, message,
room.roomId, room.roomId,
room.roomSummary?.displayName ?: room.roomId, room.roomSummary()?.displayName ?: room.roomId,
room.roomSummary?.isDirect == true room.roomSummary()?.isDirect == true
) )
notifiableMessageEvent.outGoingMessage = true notifiableMessageEvent.outGoingMessage = true



View File

@ -86,7 +86,7 @@ class RoomDirectoryViewModel @AssistedInject constructor(@Assisted initialState:
private fun observeJoinedRooms() { private fun observeJoinedRooms() {
session session
.rx() .rx()
.liveRoomSummaries() .liveRoomSummaries(fetchLastEvents = false)
.subscribe { list -> .subscribe { list ->
val joinedRoomIds = list val joinedRoomIds = list
// Keep only joined room // Keep only joined room

View File

@ -54,7 +54,7 @@ class RoomPreviewViewModel @AssistedInject constructor(@Assisted initialState: R
private fun observeJoinedRooms() { private fun observeJoinedRooms() {
session session
.rx() .rx()
.liveRoomSummaries() .liveRoomSummaries(fetchLastEvents = false)
.subscribe { list -> .subscribe { list ->
withState { state -> withState { state ->
val isRoomJoined = list val isRoomJoined = list

View File

@ -23,6 +23,7 @@ import android.preference.PreferenceManager
import android.text.TextUtils import android.text.TextUtils
import android.util.Pair import android.util.Pair
import androidx.core.content.edit import androidx.core.content.edit
import kotlinx.coroutines.Dispatchers
import im.vector.riotx.R import im.vector.riotx.R
import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -75,7 +76,7 @@ object VectorLocale {
} }


// init the known locales in background, using kotlin coroutines // init the known locales in background, using kotlin coroutines
GlobalScope.launch { GlobalScope.launch(Dispatchers.IO) {
initApplicationLocales(context) initApplicationLocales(context)
} }
} }

View File

@ -86,9 +86,7 @@ class VectorSettingsActivity : VectorBaseActivity(),
try { try {
pref?.fragment?.let { pref?.fragment?.let {
oFragment = supportFragmentManager.fragmentFactory oFragment = supportFragmentManager.fragmentFactory
.instantiate( .instantiate(classLoader, it)
classLoader,
it, pref.extras)
} }
} catch (e: Throwable) { } catch (e: Throwable) {
showSnackbar(getString(R.string.not_implemented)) showSnackbar(getString(R.string.not_implemented))