forked from GitHub-Mirror/riotX-android
First attempt to handle room name + make app listen to room summaries instead of rooms
This commit is contained in:
parent
279241974a
commit
f747d268c9
@ -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())
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
||||||
|
@ -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) {
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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>>()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
}
|
}
|
@ -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>>
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
package im.vector.matrix.android.api.session.room.model
|
||||||
|
|
||||||
|
enum class MyMembership {
|
||||||
|
JOINED,
|
||||||
|
LEFT,
|
||||||
|
INVITED,
|
||||||
|
NONE
|
||||||
|
}
|
@ -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()
|
||||||
|
)
|
@ -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
|
||||||
|
)
|
@ -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
|
||||||
)
|
)
|
@ -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 = ""
|
||||||
|
)
|
@ -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)
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
}
|
@ -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>()!! }
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
}
|
@ -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 {
|
||||||
|
@ -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() {
|
||||||
|
@ -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) {
|
||||||
|
@ -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)
|
||||||
}
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -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() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>,
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
)
|
)
|
@ -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
|
||||||
|
)
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user