First attempt to handle room name + make app listen to room summaries instead of rooms

This commit is contained in:
ganfra 2018-10-23 18:25:28 +02:00
parent 279241974a
commit f747d268c9
40 changed files with 568 additions and 144 deletions

View File

@ -3,6 +3,7 @@ package im.vector.riotredesign
import android.app.Application import android.app.Application
import im.vector.matrix.android.BuildConfig import im.vector.matrix.android.BuildConfig
import im.vector.riotredesign.core.di.AppModule import im.vector.riotredesign.core.di.AppModule
import org.koin.log.EmptyLogger
import org.koin.standalone.StandAloneContext.startKoin import org.koin.standalone.StandAloneContext.startKoin
import timber.log.Timber import timber.log.Timber
@ -13,7 +14,7 @@ class Riot : Application() {
if (BuildConfig.DEBUG) { if (BuildConfig.DEBUG) {
Timber.plant(Timber.DebugTree()) Timber.plant(Timber.DebugTree())
} }
startKoin(listOf(AppModule(this))) startKoin(listOf(AppModule(this)), logger = EmptyLogger())
} }
} }

View File

@ -5,11 +5,11 @@ import im.vector.matrix.android.api.session.events.model.EnrichedEvent
class EventDiffUtilCallback : DiffUtil.ItemCallback<EnrichedEvent>() { class EventDiffUtilCallback : DiffUtil.ItemCallback<EnrichedEvent>() {
override fun areItemsTheSame(p0: EnrichedEvent, p1: EnrichedEvent): Boolean { override fun areItemsTheSame(p0: EnrichedEvent, p1: EnrichedEvent): Boolean {
return p0.core.eventId == p1.core.eventId return p0.root.eventId == p1.root.eventId
} }
override fun areContentsTheSame(p0: EnrichedEvent, p1: EnrichedEvent): Boolean { override fun areContentsTheSame(p0: EnrichedEvent, p1: EnrichedEvent): Boolean {
return p0.core == p1.core return p0.root == p1.root
&& p0.getMetaEvents() && p0.getMetaEvents()
.zip(p1.getMetaEvents()) { a, b -> .zip(p1.getMetaEvents()) { a, b ->
a.eventId == b.eventId a.eventId == b.eventId

View File

@ -1,20 +0,0 @@
package im.vector.riotredesign.features.home
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.room.Room
class RoomController(private val callback: Callback? = null) : TypedEpoxyController<List<Room>>() {
override fun buildModels(data: List<Room>?) {
data?.forEach {
RoomItem(it.roomId, listener = { callback?.onRoomSelected(it) })
.id(it.roomId)
.addTo(this)
}
}
interface Callback {
fun onRoomSelected(room: Room)
}
}

View File

@ -55,9 +55,10 @@ class RoomDetailFragment : RiotFragment(), TimelineEventAdapter.Callback {
layoutManager.stackFromEnd = true layoutManager.stackFromEnd = true
timelineAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() { timelineAdapter.registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
override fun onItemRangeInserted(positionStart: Int, itemCount: Int) { override fun onItemRangeInserted(positionStart: Int, itemCount: Int) {
if (layoutManager.findFirstVisibleItemPosition() == 0) { /*if (layoutManager.findFirstVisibleItemPosition() == 0) {
layoutManager.scrollToPosition(0) layoutManager.scrollToPosition(0)
} }
*/
} }
}) })
recyclerView.layoutManager = layoutManager recyclerView.layoutManager = layoutManager

View File

@ -5,7 +5,7 @@ import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.KotlinModel import im.vector.riotredesign.core.epoxy.KotlinModel
data class RoomItem( data class RoomItem(
val title: String, val title: CharSequence,
val listener: (() -> Unit)? = null val listener: (() -> Unit)? = null
) : KotlinModel(R.layout.item_room) { ) : KotlinModel(R.layout.item_room) {

View File

@ -2,19 +2,18 @@ package im.vector.riotredesign.features.home
import android.arch.lifecycle.Observer import android.arch.lifecycle.Observer
import android.os.Bundle import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.Matrix
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.extensions.addFragmentToBackstack import im.vector.riotredesign.core.extensions.addFragmentToBackstack
import im.vector.riotredesign.core.platform.RiotFragment import im.vector.riotredesign.core.platform.RiotFragment
import kotlinx.android.synthetic.main.fragment_room_list.* import kotlinx.android.synthetic.main.fragment_room_list.*
import org.koin.android.ext.android.inject import org.koin.android.ext.android.inject
class RoomListFragment : RiotFragment(), RoomController.Callback { class RoomListFragment : RiotFragment(), RoomSummaryController.Callback {
companion object { companion object {
@ -26,7 +25,7 @@ class RoomListFragment : RiotFragment(), RoomController.Callback {
private val matrix by inject<Matrix>() private val matrix by inject<Matrix>()
private val currentSession = matrix.currentSession!! private val currentSession = matrix.currentSession!!
private val roomController = RoomController(this) private lateinit var roomController: RoomSummaryController
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_room_list, container, false) return inflater.inflate(R.layout.fragment_room_list, container, false)
@ -34,15 +33,16 @@ class RoomListFragment : RiotFragment(), RoomController.Callback {
override fun onActivityCreated(savedInstanceState: Bundle?) { override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState) super.onActivityCreated(savedInstanceState)
roomController = RoomSummaryController(this)
epoxyRecyclerView.setController(roomController) epoxyRecyclerView.setController(roomController)
currentSession.liveRooms().observe(this, Observer<List<Room>> { renderRooms(it) }) currentSession.liveRoomSummaries().observe(this, Observer<List<RoomSummary>> { renderRooms(it) })
} }
private fun renderRooms(rooms: List<Room>?) { private fun renderRooms(rooms: List<RoomSummary>?) {
roomController.setData(rooms) roomController.setData(rooms)
} }
override fun onRoomSelected(room: Room) { override fun onRoomSelected(room: RoomSummary) {
val detailFragment = RoomDetailFragment.newInstance(room.roomId) val detailFragment = RoomDetailFragment.newInstance(room.roomId)
addFragmentToBackstack(detailFragment, R.id.homeFragmentContainer) addFragmentToBackstack(detailFragment, R.id.homeFragmentContainer)
} }

View File

@ -0,0 +1,21 @@
package im.vector.riotredesign.features.home
import com.airbnb.epoxy.TypedEpoxyController
import im.vector.matrix.android.api.session.room.model.RoomSummary
class RoomSummaryController(private val callback: Callback? = null
) : TypedEpoxyController<List<RoomSummary>>() {
override fun buildModels(data: List<RoomSummary>?) {
data?.forEach {
RoomItem(it.displayName, listener = { callback?.onRoomSelected(it) })
.id(it.roomId)
.addTo(this)
}
}
interface Callback {
fun onRoomSelected(room: RoomSummary)
}
}

View File

@ -38,10 +38,10 @@ class TimelineEventAdapter(private val callback: Callback? = null)
val titleView = view.findViewById<TextView>(R.id.titleView)!! val titleView = view.findViewById<TextView>(R.id.titleView)!!
fun bind(event: EnrichedEvent?) { fun bind(event: EnrichedEvent?) {
if (event == null || event.core.type != EventType.MESSAGE) { if (event == null) {
titleView.text = null titleView.text = null
} else { } else if (event.root.type == EventType.MESSAGE) {
val messageContent = event.core.content<MessageContent>() val messageContent = event.root.content<MessageContent>()
val roomMember = event.getMetaEvents(EventType.STATE_ROOM_MEMBER).firstOrNull()?.content<RoomMember>() val roomMember = event.getMetaEvents(EventType.STATE_ROOM_MEMBER).firstOrNull()?.content<RoomMember>()
if (messageContent == null || roomMember == null) { if (messageContent == null || roomMember == null) {
titleView.text = null titleView.text = null
@ -49,6 +49,8 @@ class TimelineEventAdapter(private val callback: Callback? = null)
val text = "${roomMember.displayName} : ${messageContent.body}" val text = "${roomMember.displayName} : ${messageContent.body}"
titleView.text = text titleView.text = text
} }
} else {
titleView.text = event.root.toString()
} }
} }
} }

View File

@ -2,11 +2,12 @@ package im.vector.matrix.android.api.session
import android.support.annotation.MainThread import android.support.annotation.MainThread
import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.internal.database.SessionRealmHolder import im.vector.matrix.android.internal.auth.data.SessionParams
import im.vector.matrix.android.internal.session.sync.job.SyncThread
interface Session : RoomService { interface Session : RoomService {
val sessionParams: SessionParams
@MainThread @MainThread
fun open() fun open()

View File

@ -1,6 +1,6 @@
package im.vector.matrix.android.api.session.events.model package im.vector.matrix.android.api.session.events.model
data class EnrichedEvent(val core: Event) { data class EnrichedEvent(val root: Event) {
private val metaEventsByType = HashMap<String, ArrayList<Event>>() private val metaEventsByType = HashMap<String, ArrayList<Event>>()

View File

@ -3,11 +3,16 @@ package im.vector.matrix.android.api.session.room
import android.arch.lifecycle.LiveData import android.arch.lifecycle.LiveData
import android.arch.paging.PagedList import android.arch.paging.PagedList
import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.events.model.EnrichedEvent
import im.vector.matrix.android.api.session.room.model.MyMembership
interface Room { interface Room {
val roomId: String val roomId: String
val myMembership: MyMembership
fun liveTimeline(): LiveData<PagedList<EnrichedEvent>> fun liveTimeline(): LiveData<PagedList<EnrichedEvent>>
fun getNumberOfJoinedMembers(): Int
} }

View File

@ -1,6 +1,7 @@
package im.vector.matrix.android.api.session.room package im.vector.matrix.android.api.session.room
import android.arch.lifecycle.LiveData import android.arch.lifecycle.LiveData
import im.vector.matrix.android.api.session.room.model.RoomSummary
interface RoomService { interface RoomService {
@ -10,4 +11,7 @@ interface RoomService {
fun liveRooms(): LiveData<List<Room>> fun liveRooms(): LiveData<List<Room>>
fun liveRoomSummaries(): LiveData<List<RoomSummary>>
} }

View File

@ -0,0 +1,8 @@
package im.vector.matrix.android.api.session.room.model
enum class MyMembership {
JOINED,
LEFT,
INVITED,
NONE
}

View File

@ -0,0 +1,9 @@
package im.vector.matrix.android.api.session.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class RoomAliasesContent(
@Json(name = "aliases") val aliases: List<String> = emptyList()
)

View File

@ -0,0 +1,9 @@
package im.vector.matrix.android.api.session.room.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class RoomCanonicalAliasContent(
@Json(name = "alias") val canonicalAlias: String? = null
)

View File

@ -5,5 +5,5 @@ import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true) @JsonClass(generateAdapter = true)
data class RoomNameContent( data class RoomNameContent(
@Json(name = "name") val name: String @Json(name = "name") val name: String? = null
) )

View File

@ -0,0 +1,7 @@
package im.vector.matrix.android.api.session.room.model
data class RoomSummary(
val roomId: String,
var displayName: String = "",
var topic: String = ""
)

View File

@ -0,0 +1,21 @@
package im.vector.matrix.android.internal.database.mapper
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.internal.database.model.RoomEntity
import im.vector.matrix.android.internal.session.room.DefaultRoom
object RoomMapper {
internal fun map(roomEntity: RoomEntity): Room {
return DefaultRoom(
roomEntity.roomId,
roomEntity.membership
)
}
}
fun RoomEntity.asDomain(): Room {
return RoomMapper.map(this)
}

View File

@ -0,0 +1,20 @@
package im.vector.matrix.android.internal.database.mapper
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
object RoomSummaryMapper {
internal fun map(roomSummaryEntity: RoomSummaryEntity): RoomSummary {
return RoomSummary(
roomSummaryEntity.roomId,
roomSummaryEntity.displayName ?: "",
roomSummaryEntity.topic ?: ""
)
}
}
fun RoomSummaryEntity.asDomain(): RoomSummary {
return RoomSummaryMapper.map(this)
}

View File

@ -1,5 +1,6 @@
package im.vector.matrix.android.internal.database.model package im.vector.matrix.android.internal.database.model
import im.vector.matrix.android.api.session.room.model.MyMembership
import io.realm.RealmList import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.Ignore import io.realm.annotations.Ignore
@ -10,19 +11,12 @@ open class RoomEntity(@PrimaryKey var roomId: String = "",
var chunks: RealmList<ChunkEntity> = RealmList() var chunks: RealmList<ChunkEntity> = RealmList()
) : RealmObject() { ) : RealmObject() {
private var membershipStr: String = Membership.NONE.name private var membershipStr: String = MyMembership.NONE.name
@delegate:Ignore var membership: Membership by Delegates.observable(Membership.valueOf(membershipStr)) { _, _, newValue -> @delegate:Ignore var membership: MyMembership by Delegates.observable(MyMembership.valueOf(membershipStr)) { _, _, newValue ->
membershipStr = newValue.name membershipStr = newValue.name
} }
companion object; companion object;
enum class Membership {
JOINED,
LEFT,
INVITED,
NONE
}
} }

View File

@ -1,5 +1,6 @@
package im.vector.matrix.android.internal.database.model package im.vector.matrix.android.internal.database.model
import io.realm.RealmList
import io.realm.RealmObject import io.realm.RealmObject
import io.realm.annotations.PrimaryKey import io.realm.annotations.PrimaryKey
@ -7,5 +8,12 @@ import io.realm.annotations.PrimaryKey
open class RoomSummaryEntity(@PrimaryKey var roomId: String = "", open class RoomSummaryEntity(@PrimaryKey var roomId: String = "",
var displayName: String? = "", var displayName: String? = "",
var topic: String? = "", var topic: String? = "",
var lastMessage: EventEntity? = null var lastMessage: EventEntity? = null,
) : RealmObject() var heroes: RealmList<String> = RealmList(),
var joinedMembersCount: Int? = 0,
var invitedMembersCount: Int? = 0
) : RealmObject() {
companion object
}

View File

@ -1,26 +1,43 @@
package im.vector.matrix.android.internal.database.query package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.RoomMember
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.EventEntity
import io.realm.Realm import io.realm.Realm
import io.realm.RealmQuery import io.realm.RealmQuery
import io.realm.Sort import io.realm.Sort
fun EventEntity.Companion.where(realm: Realm, roomId: String, type: String? = null): RealmQuery<EventEntity> { fun EventEntity.Companion.where(realm: Realm, roomId: String, type: String? = null): RealmQuery<EventEntity> {
var query = realm.where(EventEntity::class.java) val query = realm.where(EventEntity::class.java)
.equalTo("chunk.room.roomId", roomId) .equalTo("chunk.room.roomId", roomId)
if (type != null) { if (type != null) {
query = query.equalTo("type", type) query.equalTo("type", type)
} }
return query return query
} }
fun EventEntity.Companion.stateEvents(realm: Realm, roomId: String): RealmQuery<EventEntity> {
return realm.where(EventEntity::class.java)
.equalTo("chunk.room.roomId", roomId)
.isNotNull("stateKey")
}
fun RealmQuery<EventEntity>.last(from: Long? = null): EventEntity? { fun RealmQuery<EventEntity>.last(from: Long? = null): EventEntity? {
var query = this
if (from != null) { if (from != null) {
query = query.lessThanOrEqualTo("originServerTs", from) this.lessThanOrEqualTo("originServerTs", from)
} }
return query return this
.sort("originServerTs", Sort.DESCENDING) .sort("originServerTs", Sort.DESCENDING)
.findFirst() .findFirst()
} }
fun EventEntity.Companion.findAllRoomMembers(realm: Realm, roomId: String): Map<String, RoomMember> {
return EventEntity
.where(realm, roomId, EventType.STATE_ROOM_MEMBER)
.sort("originServerTs")
.findAll()
.map { it.asDomain() }
.associateBy { it.stateKey!! }
.mapValues { it.value.content<RoomMember>()!! }
}

View File

@ -1,5 +1,6 @@
package im.vector.matrix.android.internal.database.query package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.api.session.room.model.MyMembership
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.RealmQuery import io.realm.RealmQuery
@ -8,7 +9,7 @@ fun RoomEntity.Companion.where(realm: Realm, roomId: String): RealmQuery<RoomEnt
return realm.where<RoomEntity>(RoomEntity::class.java).equalTo("roomId", roomId) return realm.where<RoomEntity>(RoomEntity::class.java).equalTo("roomId", roomId)
} }
fun RoomEntity.Companion.where(realm: Realm, membership: RoomEntity.Membership? = null): RealmQuery<RoomEntity> { fun RoomEntity.Companion.where(realm: Realm, membership: MyMembership? = null): RealmQuery<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)

View File

@ -0,0 +1,13 @@
package im.vector.matrix.android.internal.database.query
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import io.realm.Realm
import io.realm.RealmQuery
fun RoomSummaryEntity.Companion.where(realm: Realm, roomId: String? = null): RealmQuery<RoomSummaryEntity> {
val query = realm.where(RoomSummaryEntity::class.java)
if (roomId != null) {
query.equalTo("roomId", roomId)
}
return query
}

View File

@ -25,7 +25,9 @@ class NetworkModule : Module {
single { single {
val logger = HttpLoggingInterceptor.Logger { message -> Timber.v(message) } val logger = HttpLoggingInterceptor.Logger { message -> Timber.v(message) }
HttpLoggingInterceptor(logger).apply { level = HttpLoggingInterceptor.Level.BASIC } val interceptor = HttpLoggingInterceptor(logger)
interceptor.level = HttpLoggingInterceptor.Level.BASIC
interceptor
} }
single { single {

View File

@ -6,9 +6,10 @@ 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.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.internal.auth.data.SessionParams import im.vector.matrix.android.internal.auth.data.SessionParams
import im.vector.matrix.android.internal.session.room.RoomModule import im.vector.matrix.android.internal.session.room.RoomModule
import im.vector.matrix.android.internal.session.room.RoomSummaryObserver import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
import im.vector.matrix.android.internal.session.sync.SyncModule import im.vector.matrix.android.internal.session.sync.SyncModule
import im.vector.matrix.android.internal.session.sync.job.SyncThread import im.vector.matrix.android.internal.session.sync.job.SyncThread
import org.koin.core.scope.Scope import org.koin.core.scope.Scope
@ -18,7 +19,7 @@ import org.koin.standalone.getKoin
import org.koin.standalone.inject import org.koin.standalone.inject
class DefaultSession(private val sessionParams: SessionParams) : Session, KoinComponent, RoomService { class DefaultSession(override val sessionParams: SessionParams) : Session, KoinComponent, RoomService {
companion object { companion object {
const val SCOPE: String = "session" const val SCOPE: String = "session"
@ -26,7 +27,7 @@ class DefaultSession(private val sessionParams: SessionParams) : Session, KoinCo
private lateinit var scope: Scope private lateinit var scope: Scope
private val roomSummaryObserver by inject<RoomSummaryObserver>() private val roomSummaryObserver by inject<RoomSummaryUpdater>()
private val roomService by inject<RoomService>() private val roomService by inject<RoomService>()
private val syncThread by inject<SyncThread>() private val syncThread by inject<SyncThread>()
private var isOpen = false private var isOpen = false
@ -70,6 +71,10 @@ class DefaultSession(private val sessionParams: SessionParams) : Session, KoinCo
return roomService.liveRooms() return roomService.liveRooms()
} }
override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
return roomService.liveRoomSummaries()
}
// Private methods ***************************************************************************** // Private methods *****************************************************************************
private fun checkIsMainThread() { private fun checkIsMainThread() {

View File

@ -7,7 +7,9 @@ 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.room.DefaultRoomService import im.vector.matrix.android.internal.session.room.DefaultRoomService
import im.vector.matrix.android.internal.session.room.RoomSummaryObserver import im.vector.matrix.android.internal.session.room.RoomDisplayNameResolver
import im.vector.matrix.android.internal.session.room.RoomMemberDisplayNameResolver
import im.vector.matrix.android.internal.session.room.RoomSummaryUpdater
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
@ -34,7 +36,15 @@ class SessionModule(private val sessionParams: SessionParams) : Module {
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
RoomSummaryObserver(get()) RoomMemberDisplayNameResolver()
}
scope(DefaultSession.SCOPE) {
RoomDisplayNameResolver(get(), get(), sessionParams)
}
scope(DefaultSession.SCOPE) {
RoomSummaryUpdater(get(), get(), get())
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {

View File

@ -1,6 +1,7 @@
package im.vector.matrix.android.api.session.events.interceptor package im.vector.matrix.android.internal.session.events.interceptor
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor
import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.events.model.EnrichedEvent
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.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.mapper.asDomain
@ -11,15 +12,15 @@ import im.vector.matrix.android.internal.database.query.where
class MessageEventInterceptor(val monarchy: Monarchy) : EnrichedEventInterceptor { class MessageEventInterceptor(val monarchy: Monarchy) : EnrichedEventInterceptor {
override fun canEnrich(event: EnrichedEvent): Boolean { override fun canEnrich(event: EnrichedEvent): Boolean {
return event.core.type == EventType.MESSAGE return event.root.type == EventType.MESSAGE
} }
override fun enrich(roomId: String, event: EnrichedEvent) { override fun enrich(roomId: String, event: EnrichedEvent) {
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->
val roomMember = EventEntity val roomMember = EventEntity
.where(realm, roomId, EventType.STATE_ROOM_MEMBER) .where(realm, roomId, EventType.STATE_ROOM_MEMBER)
.equalTo("stateKey", event.core.sender) .equalTo("stateKey", event.root.sender)
.last(from = event.core.originServerTs) .last(from = event.root.originServerTs)
?.asDomain() ?.asDomain()
event.enrichWith(roomMember) event.enrichWith(roomMember)
} }

View File

@ -5,12 +5,14 @@ import android.arch.paging.LivePagedListBuilder
import android.arch.paging.PagedList import android.arch.paging.PagedList
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor import im.vector.matrix.android.api.session.events.interceptor.EnrichedEventInterceptor
import im.vector.matrix.android.api.session.events.interceptor.MessageEventInterceptor
import im.vector.matrix.android.api.session.events.model.EnrichedEvent import im.vector.matrix.android.api.session.events.model.EnrichedEvent
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.MyMembership
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.ChunkEntity import im.vector.matrix.android.internal.database.model.ChunkEntity
import im.vector.matrix.android.internal.database.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
import im.vector.matrix.android.internal.session.events.interceptor.MessageEventInterceptor
import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest import im.vector.matrix.android.internal.session.room.timeline.PaginationRequest
import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback import im.vector.matrix.android.internal.session.room.timeline.TimelineBoundaryCallback
import io.realm.Sort import io.realm.Sort
@ -19,7 +21,8 @@ import org.koin.standalone.inject
import java.util.concurrent.Executors import java.util.concurrent.Executors
data class DefaultRoom( data class DefaultRoom(
override val roomId: String override val roomId: String,
override val myMembership: MyMembership
) : Room, KoinComponent { ) : Room, KoinComponent {
private val paginationRequest by inject<PaginationRequest>() private val paginationRequest by inject<PaginationRequest>()
@ -63,4 +66,10 @@ data class DefaultRoom(
return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder) return monarchy.findAllPagedWithChanges(realmDataSourceFactory, livePagedListBuilder)
} }
override fun getNumberOfJoinedMembers(): Int {
val roomSummary = monarchy.fetchAllCopiedSync { realm -> RoomSummaryEntity.where(realm, roomId) }.firstOrNull()
return roomSummary?.joinedMembersCount ?: 0
}
} }

View File

@ -4,7 +4,10 @@ import android.arch.lifecycle.LiveData
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.RoomService import im.vector.matrix.android.api.session.room.RoomService
import im.vector.matrix.android.api.session.room.model.RoomSummary
import im.vector.matrix.android.internal.database.mapper.asDomain
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.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.where import im.vector.matrix.android.internal.database.query.where
class DefaultRoomService(private val monarchy: Monarchy) : RoomService { class DefaultRoomService(private val monarchy: Monarchy) : RoomService {
@ -12,7 +15,7 @@ class DefaultRoomService(private val monarchy: Monarchy) : RoomService {
override fun getAllRooms(): List<Room> { override fun getAllRooms(): List<Room> {
var rooms: List<Room> = emptyList() var rooms: List<Room> = emptyList()
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->
rooms = RoomEntity.where(realm).findAll().map { DefaultRoom(it.roomId) } rooms = RoomEntity.where(realm).findAll().map { it.asDomain() }
} }
return rooms return rooms
} }
@ -20,7 +23,7 @@ class DefaultRoomService(private val monarchy: Monarchy) : RoomService {
override fun getRoom(roomId: String): Room? { override fun getRoom(roomId: String): Room? {
var room: Room? = null var room: Room? = null
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->
room = RoomEntity.where(realm, roomId).findFirst()?.let { DefaultRoom(it.roomId) } room = RoomEntity.where(realm, roomId).findFirst()?.let { it.asDomain() }
} }
return room return room
} }
@ -28,8 +31,16 @@ class DefaultRoomService(private val monarchy: Monarchy) : RoomService {
override fun liveRooms(): LiveData<List<Room>> { override fun liveRooms(): LiveData<List<Room>> {
return monarchy.findAllMappedWithChanges( return monarchy.findAllMappedWithChanges(
{ realm -> RoomEntity.where(realm) }, { realm -> RoomEntity.where(realm) },
{ DefaultRoom(it.roomId) } { it.asDomain() }
) )
} }
override fun liveRoomSummaries(): LiveData<List<RoomSummary>> {
return monarchy.findAllMappedWithChanges(
{ realm -> RoomSummaryEntity.where(realm) },
{ it.asDomain() }
)
}
} }

View File

@ -0,0 +1,130 @@
/*
* Copyright 2018 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.matrix.android.internal.session.room
import android.content.Context
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.R
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.api.session.room.model.RoomAliasesContent
import im.vector.matrix.android.api.session.room.model.RoomCanonicalAliasContent
import im.vector.matrix.android.api.session.room.model.RoomNameContent
import im.vector.matrix.android.internal.auth.data.SessionParams
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.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.findAllRoomMembers
import im.vector.matrix.android.internal.database.query.last
import im.vector.matrix.android.internal.database.query.where
/**
* This class computes room display name
*/
class RoomDisplayNameResolver(private val monarchy: Monarchy,
private val roomMemberDisplayNameResolver: RoomMemberDisplayNameResolver,
private val sessionParams: SessionParams
) {
/**
* Compute the room display name
*
* @param context
* @param room: the room to resolve the name of.
* @return the room display name
*/
fun resolve(context: Context, room: Room): CharSequence {
// this algorithm is the one defined in
// https://github.com/matrix-org/matrix-js-sdk/blob/develop/lib/models/room.js#L617
// calculateRoomName(room, userId)
// For Lazy Loaded room, see algorithm here:
// https://docs.google.com/document/d/11i14UI1cUz-OJ0knD5BFu7fmT6Fo327zvMYqfSAR7xs/edit#heading=h.qif6pkqyjgzn
var name: CharSequence? = null
monarchy.doWithRealm { realm ->
val roomName = EventEntity.where(realm, room.roomId, EventType.STATE_ROOM_NAME).last()?.asDomain()
name = roomName?.content<RoomNameContent>()?.name
if (!name.isNullOrEmpty()) {
return@doWithRealm
}
val canonicalAlias = EventEntity.where(realm, room.roomId, EventType.STATE_CANONICAL_ALIAS).last()?.asDomain()
name = canonicalAlias?.content<RoomCanonicalAliasContent>()?.canonicalAlias
if (!name.isNullOrEmpty()) {
return@doWithRealm
}
val aliases = EventEntity.where(realm, room.roomId, EventType.STATE_ROOM_ALIASES).last()?.asDomain()
name = aliases?.content<RoomAliasesContent>()?.aliases?.firstOrNull()
if (!name.isNullOrEmpty()) {
return@doWithRealm
}
val otherRoomMembers = EventEntity
.findAllRoomMembers(realm, room.roomId)
.filterKeys { it != sessionParams.credentials.userId }
if (room.myMembership == MyMembership.INVITED) {
//TODO handle invited
/*
if (currentUser != null
&& !othersActiveMembers.isEmpty()
&& !TextUtils.isEmpty(currentUser!!.mSender)) {
// extract who invited us to the room
name = context.getString(R.string.room_displayname_invite_from, roomState.resolve(currentUser!!.mSender))
} else {
name = context.getString(R.string.room_displayname_room_invite)
}
*/
name = context.getString(R.string.room_displayname_room_invite)
} else {
val roomSummary = RoomSummaryEntity.where(realm, room.roomId).findFirst()
val memberIds = if (roomSummary?.heroes?.isNotEmpty() == true) {
roomSummary.heroes
} else {
otherRoomMembers.keys.toList()
}
val nbOfOtherMembers = memberIds.size
when (nbOfOtherMembers) {
0 -> name = context.getString(R.string.room_displayname_empty_room)
1 -> name = roomMemberDisplayNameResolver.resolve(memberIds[0], otherRoomMembers)
2 -> {
val member1 = memberIds[0]
val member2 = memberIds[1]
name = context.getString(R.string.room_displayname_two_members,
roomMemberDisplayNameResolver.resolve(member1, otherRoomMembers),
roomMemberDisplayNameResolver.resolve(member2, otherRoomMembers)
)
}
else -> {
val member = memberIds[0]
name = context.resources.getQuantityString(R.plurals.room_displayname_three_and_more_members,
room.getNumberOfJoinedMembers() - 1,
roomMemberDisplayNameResolver.resolve(member, otherRoomMembers),
room.getNumberOfJoinedMembers() - 1)
}
}
}
return@doWithRealm
}
return name ?: room.roomId
}
}

View File

@ -0,0 +1,37 @@
package im.vector.matrix.android.internal.session.room
import im.vector.matrix.android.api.session.room.model.RoomMember
class RoomMemberDisplayNameResolver {
fun resolve(userId: String, members: Map<String, RoomMember>): String? {
var displayName: String? = null
val currentMember = members[userId]
// Get the user display name from the member list of the room
// Do not consider null display name
if (currentMember != null && !currentMember.displayName.isNullOrEmpty()) {
val hasNameCollision = members
.filterValues { it != currentMember && it.displayName == currentMember.displayName }
.isNotEmpty()
displayName = if (hasNameCollision) {
"${currentMember.displayName} ( $userId )"
} else {
currentMember.displayName
}
}
// TODO handle invited users
/*else if (null != member && TextUtils.equals(member!!.membership, RoomMember.MEMBERSHIP_INVITE)) {
val user = (mDataHandler as MXDataHandler).getUser(userId)
if (null != user) {
displayName = user!!.displayname
}
}
*/
if (displayName == null) {
// By default, use the user ID
displayName = userId
}
return displayName
}
}

View File

@ -1,63 +0,0 @@
package im.vector.matrix.android.internal.session.room
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.model.RoomNameContent
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
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.last
import im.vector.matrix.android.internal.database.query.where
import io.realm.RealmResults
import java.util.concurrent.atomic.AtomicBoolean
internal class RoomSummaryObserver(private val monarchy: Monarchy) {
private lateinit var roomResults: RealmResults<RoomEntity>
private var isStarted = AtomicBoolean(false)
fun start() {
if (isStarted.compareAndSet(false, true)) {
monarchy.doWithRealm {
roomResults = RoomEntity.where(it).findAllAsync()
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) {
monarchy.writeAsync { realm ->
val lastNameEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_NAME).last()?.asDomain()
val lastTopicEvent = EventEntity.where(realm, roomId, EventType.STATE_ROOM_TOPIC).last()?.asDomain()
val lastMessageEvent = EventEntity.where(realm, roomId, EventType.MESSAGE).last()
val roomSummary = realm.copyToRealmOrUpdate(RoomSummaryEntity(roomId))
roomSummary.displayName = lastNameEvent?.content<RoomNameContent>()?.name
roomSummary.topic = lastTopicEvent?.content<RoomTopicContent>()?.topic
roomSummary.lastMessage = lastMessageEvent
}
}
}

View File

@ -0,0 +1,80 @@
package im.vector.matrix.android.internal.session.room
import android.arch.lifecycle.Observer
import android.content.Context
import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.room.Room
import im.vector.matrix.android.api.session.room.model.RoomTopicContent
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.last
import im.vector.matrix.android.internal.database.query.where
import io.realm.RealmResults
import timber.log.Timber
import java.util.concurrent.atomic.AtomicBoolean
internal class RoomSummaryUpdater(private val monarchy: Monarchy,
private val roomDisplayNameResolver: RoomDisplayNameResolver,
private val context: Context
) : Observer<Monarchy.ManagedChangeSet<RoomEntity>> {
private var isStarted = AtomicBoolean(false)
private val liveResults = monarchy.findAllManagedWithChanges { RoomEntity.where(it) }
fun start() {
if (isStarted.compareAndSet(false, true)) {
liveResults.observeForever(this)
}
}
fun dispose() {
if (isStarted.compareAndSet(true, false)) {
liveResults.removeObserver(this)
}
}
// PRIVATE
override fun onChanged(changeSet: Monarchy.ManagedChangeSet<RoomEntity>?) {
if (changeSet == null) {
return
}
manageRoomResults(changeSet.realmResults, changeSet.orderedCollectionChangeSet.changes)
manageRoomResults(changeSet.realmResults, changeSet.orderedCollectionChangeSet.insertions)
}
private fun manageRoomResults(rooms: RealmResults<RoomEntity>, indexes: IntArray) {
indexes.forEach {
val room = rooms[it]?.asDomain()
try {
manageRoom(room)
} catch (e: Exception) {
Timber.e(e, "An error occured when updating room summaries")
}
}
}
private fun manageRoom(room: Room?) {
if (room == null) {
return
}
monarchy.writeAsync { realm ->
val roomSummary = RoomSummaryEntity.where(realm, room.roomId).findFirst()
?: RoomSummaryEntity(room.roomId)
val lastMessageEvent = EventEntity.where(realm, room.roomId, EventType.MESSAGE).last()
val lastTopicEvent = EventEntity.where(realm, room.roomId, EventType.STATE_ROOM_TOPIC).last()?.asDomain()
roomSummary.displayName = roomDisplayNameResolver.resolve(context, room).toString()
roomSummary.topic = lastTopicEvent?.content<RoomTopicContent>()?.topic
roomSummary.lastMessage = lastMessageEvent
}
}
}

View File

@ -28,10 +28,10 @@ class TimelineBoundaryCallback(private val paginationRequest: PaginationRequest,
override fun onItemAtEndLoaded(itemAtEnd: EnrichedEvent) { override fun onItemAtEndLoaded(itemAtEnd: EnrichedEvent) {
helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) { helper.runIfNotRunning(PagingRequestHelper.RequestType.AFTER) {
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->
if (itemAtEnd.core.eventId == null) { if (itemAtEnd.root.eventId == null) {
return@doWithRealm return@doWithRealm
} }
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtEnd.core.eventId)).firstOrNull() val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtEnd.root.eventId)).firstOrNull()
paginationRequest.execute(roomId, chunkEntity?.prevToken, PaginationDirection.BACKWARDS, callback = createCallback(it)) paginationRequest.execute(roomId, chunkEntity?.prevToken, PaginationDirection.BACKWARDS, callback = createCallback(it))
} }
} }
@ -40,10 +40,10 @@ class TimelineBoundaryCallback(private val paginationRequest: PaginationRequest,
override fun onItemAtFrontLoaded(itemAtFront: EnrichedEvent) { override fun onItemAtFrontLoaded(itemAtFront: EnrichedEvent) {
helper.runIfNotRunning(PagingRequestHelper.RequestType.BEFORE) { helper.runIfNotRunning(PagingRequestHelper.RequestType.BEFORE) {
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->
if (itemAtFront.core.eventId == null) { if (itemAtFront.root.eventId == null) {
return@doWithRealm return@doWithRealm
} }
val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtFront.core.eventId)).firstOrNull() val chunkEntity = ChunkEntity.findAllIncludingEvents(realm, Collections.singletonList(itemAtFront.root.eventId)).firstOrNull()
paginationRequest.execute(roomId, chunkEntity?.nextToken, PaginationDirection.FORWARDS, callback = createCallback(it)) paginationRequest.execute(roomId, chunkEntity?.nextToken, PaginationDirection.FORWARDS, callback = createCallback(it))
} }
} }

View File

@ -2,14 +2,17 @@ package im.vector.matrix.android.internal.session.sync
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.session.events.model.Event import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.room.model.MyMembership
import im.vector.matrix.android.internal.database.mapper.asEntity 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.model.RoomSummaryEntity
import im.vector.matrix.android.internal.database.query.findAllIncludingEvents import im.vector.matrix.android.internal.database.query.findAllIncludingEvents
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
import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync import im.vector.matrix.android.internal.session.sync.model.InvitedRoomSync
import im.vector.matrix.android.internal.session.sync.model.RoomSync import im.vector.matrix.android.internal.session.sync.model.RoomSync
import im.vector.matrix.android.internal.session.sync.model.RoomSyncSummary
import io.realm.Realm import io.realm.Realm
@ -41,11 +44,16 @@ class RoomSyncHandler(private val monarchy: Monarchy) {
val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: RoomEntity(roomId) val roomEntity = RoomEntity.where(realm, roomId).findFirst() ?: RoomEntity(roomId)
if (roomEntity.membership == RoomEntity.Membership.INVITED) { if (roomEntity.membership == MyMembership.INVITED) {
roomEntity.chunks.deleteAllFromRealm() roomEntity.chunks.deleteAllFromRealm()
} }
roomEntity.membership = RoomEntity.Membership.JOINED
roomEntity.membership = MyMembership.JOINED
if (roomSync.summary != null) {
handleRoomSummary(realm, roomId, roomSync.summary)
}
if (roomSync.state != null && roomSync.state.events.isNotEmpty()) { if (roomSync.state != null && roomSync.state.events.isNotEmpty()) {
val chunkEntity = StateEventsChunkHandler().handle(realm, roomId, roomSync.state.events) val chunkEntity = StateEventsChunkHandler().handle(realm, roomId, roomSync.state.events)
if (!roomEntity.chunks.contains(chunkEntity)) { if (!roomEntity.chunks.contains(chunkEntity)) {
@ -67,7 +75,7 @@ class RoomSyncHandler(private val monarchy: Monarchy) {
InvitedRoomSync): RoomEntity { InvitedRoomSync): RoomEntity {
val roomEntity = RoomEntity() val roomEntity = RoomEntity()
roomEntity.roomId = roomId roomEntity.roomId = roomId
roomEntity.membership = RoomEntity.Membership.INVITED roomEntity.membership = MyMembership.INVITED
if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) { if (roomSync.inviteState != null && roomSync.inviteState.events.isNotEmpty()) {
val chunkEntity = handleListOfEvent(realm, roomId, roomSync.inviteState.events) val chunkEntity = handleListOfEvent(realm, roomId, roomSync.inviteState.events)
if (!roomEntity.chunks.contains(chunkEntity)) { if (!roomEntity.chunks.contains(chunkEntity)) {
@ -82,10 +90,30 @@ class RoomSyncHandler(private val monarchy: Monarchy) {
roomSync: RoomSync): RoomEntity { roomSync: RoomSync): RoomEntity {
return RoomEntity().apply { return RoomEntity().apply {
this.roomId = roomId this.roomId = roomId
this.membership = RoomEntity.Membership.LEFT this.membership = MyMembership.LEFT
} }
} }
private fun handleRoomSummary(realm: Realm,
roomId: String,
roomSummary: RoomSyncSummary) {
val roomSummaryEntity = RoomSummaryEntity.where(realm, roomId).findFirst()
?: RoomSummaryEntity(roomId)
if (roomSummary.heroes.isNotEmpty()) {
roomSummaryEntity.heroes.clear()
roomSummaryEntity.heroes.addAll(roomSummary.heroes)
}
if (roomSummary.invitedMembersCount != null) {
roomSummaryEntity.invitedMembersCount = roomSummary.invitedMembersCount
}
if (roomSummary.joinedMembersCount != null) {
roomSummaryEntity.joinedMembersCount = roomSummary.joinedMembersCount
}
realm.insertOrUpdate(roomSummaryEntity)
}
private fun handleListOfEvent(realm: Realm, private fun handleListOfEvent(realm: Realm,
roomId: String, roomId: String,
eventList: List<Event>, eventList: List<Event>,

View File

@ -44,6 +44,12 @@ data class RoomSync(
/** /**
* The notification counts for the room. * The notification counts for the room.
*/ */
@Json(name = "unread_notifications") val unreadNotifications: RoomSyncUnreadNotifications? = null @Json(name = "unread_notifications") val unreadNotifications: RoomSyncUnreadNotifications? = null,
/**
* The room summary
*/
@Json(name = "summary") val summary: RoomSyncSummary? = null
) )

View File

@ -0,0 +1,32 @@
package im.vector.matrix.android.internal.session.sync.model
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
@JsonClass(generateAdapter = true)
data class RoomSyncSummary(
/**
* Present only if the room has no m.room.name or m.room.canonical_alias.
*
*
* Lists the mxids of the first 5 members in the room who are currently joined or invited (ordered by stream ordering as seen on the server,
* to avoid it jumping around if/when topological order changes). As the heroes membership status changes, the list changes appropriately
* (sending the whole new list in the next /sync response). This list always excludes the current logged in user. If there are no joined or
* invited users, it lists the parted and banned ones instead. Servers can choose to send more or less than 5 members if they must, but 5
* seems like a good enough number for most naming purposes. Clients should use all the provided members to name the room, but may truncate
* the list if helpful for UX
*/
@Json(name = "m.heroes") val heroes: List<String> = emptyList(),
/**
* The number of m.room.members in state 'joined' (including the syncing user) (can be null)
*/
@Json(name = "m.joined_member_count") val joinedMembersCount: Int? = null,
/**
* The number of m.room.members in state 'invited' (can be null)
*/
@Json(name = "m.invited_member_count") val invitedMembersCount: Int? = null
)

View File

@ -84,4 +84,18 @@
<string name="reply_to_an_audio_file">sent an audio file.</string> <string name="reply_to_an_audio_file">sent an audio file.</string>
<string name="reply_to_a_file">sent a file.</string> <string name="reply_to_a_file">sent a file.</string>
<!-- Room display name -->
<string name="room_displayname_invite_from">Invite from %s</string>
<string name="room_displayname_room_invite">Room Invite</string>
<!-- The 2 parameters will be members' name -->
<string name="room_displayname_two_members">%1$s and %2$s</string>
<plurals name="room_displayname_three_and_more_members">
<item quantity="one">%1$s and 1 other</item>
<item quantity="other">%1$s and %2$d others</item>
</plurals>
<string name="room_displayname_empty_room">Empty room</string>
</resources> </resources>