diff --git a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt index 3d1d4cf1..48475f7d 100644 --- a/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt +++ b/app/src/main/java/im/vector/riotredesign/features/home/room/detail/RoomDetailViewModel.kt @@ -23,18 +23,20 @@ import im.vector.matrix.android.api.Matrix import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.session.Session import im.vector.matrix.android.api.session.events.model.Event +import im.vector.matrix.android.api.session.room.Room import im.vector.matrix.rx.rx import im.vector.riotredesign.core.extensions.lastMinBy import im.vector.riotredesign.core.platform.RiotViewModel import im.vector.riotredesign.features.home.room.VisibleRoomHolder import io.reactivex.rxkotlin.subscribeBy import org.koin.android.ext.android.get +import timber.log.Timber import java.util.concurrent.TimeUnit class RoomDetailViewModel(initialState: RoomDetailViewState, private val session: Session, private val visibleRoomHolder: VisibleRoomHolder -) : RiotViewModel(initialState) { +) : RiotViewModel(initialState), Room.Listener { private val room = session.getRoom(initialState.roomId)!! private val roomId = initialState.roomId @@ -57,6 +59,7 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, observeTimeline() observeDisplayedEvents() room.loadRoomMembersIfNeeded() + room.addListener(this) } fun process(action: RoomDetailActions) { @@ -67,6 +70,17 @@ class RoomDetailViewModel(initialState: RoomDetailViewState, } } + override fun onCleared() { + room.removeListener(this) + super.onCleared() + } + + // Room.Listener ******************************************************************************* + + override fun onReadReceiptsUpdated() { + Timber.v("On read receipts updated") + } + // PRIVATE METHODS ***************************************************************************** private fun handleSendMessage(action: RoomDetailActions.SendMessage) { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt index f119df94..56d66b74 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/Room.kt @@ -22,7 +22,6 @@ import im.vector.matrix.android.api.session.room.model.RoomSummary import im.vector.matrix.android.api.session.room.read.ReadService import im.vector.matrix.android.api.session.room.send.SendService import im.vector.matrix.android.api.session.room.timeline.TimelineService -import im.vector.matrix.android.api.util.Cancelable /** * This interface defines methods to interact within a room. @@ -40,4 +39,26 @@ interface Room : TimelineService, SendService, ReadService, RoomMembersService { */ val roomSummary: LiveData + /** + * Add a listener to the room. + * @param listener the listener to add. + */ + fun addListener(listener: Listener) + + /** + * Remove a listener from the room. + * @param listener the listener to remove. + */ + fun removeListener(listener: Listener) + + /** + * A listener defined at the room level to listen for some events from the different room services. + */ + interface Listener : ReadService.Listener { + + override fun onReadReceiptsUpdated() { + // no-op + } + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/read/ReadService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/read/ReadService.kt index d12bc416..5bdfe2f6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/read/ReadService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/read/ReadService.kt @@ -46,9 +46,19 @@ interface ReadService { fun readReceipts(): LiveData> /** - * @param the eventId to look for receipt. + * @param eventId to look for receipt. * * @return the receipt associated or null */ fun readReceipt(eventId: String): ReadReceipt? + /** + * A listener defined at the room level to listen for some read events. + */ + interface Listener { + /** + * Called when read receipts are updated in this room. + */ + fun onReadReceiptsUpdated() + } + } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt index c50b8303..4f8cd236 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoom.kt @@ -29,21 +29,25 @@ import im.vector.matrix.android.internal.database.mapper.asDomain import im.vector.matrix.android.internal.database.model.RoomSummaryEntity import im.vector.matrix.android.internal.database.model.RoomSummaryEntityFields import im.vector.matrix.android.internal.database.query.where +import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembersService +import im.vector.matrix.android.internal.session.room.read.DefaultReadService +import im.vector.matrix.android.internal.session.room.read.ReadServiceListeners +import im.vector.matrix.android.internal.session.room.send.DefaultSendService +import im.vector.matrix.android.internal.session.room.timeline.DefaultTimelineService -internal data class DefaultRoom( +internal class DefaultRoom( override val roomId: String, private val monarchy: Monarchy, - private val timelineService: TimelineService, - private val sendService: SendService, - private val readService: ReadService, - private val roomMembersService: RoomMembersService - - + private val timelineService: DefaultTimelineService, + private val sendService: DefaultSendService, + private val readService: DefaultReadService, + private val readServiceListeners: ReadServiceListeners, + private val roomMembersService: DefaultRoomMembersService ) : Room, - TimelineService by timelineService, - SendService by sendService, - ReadService by readService, - RoomMembersService by roomMembersService { + TimelineService by timelineService, + SendService by sendService, + ReadService by readService, + RoomMembersService by roomMembersService { override val roomSummary: LiveData by lazy { val liveData = monarchy @@ -56,5 +60,12 @@ internal data class DefaultRoom( } } + override fun addListener(listener: Room.Listener) { + readServiceListeners.addListener(listener) + } + + override fun removeListener(listener: Room.Listener) { + readServiceListeners.removeListener(listener) + } } \ No newline at end of file diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt index 3f8e5387..fa5b2f11 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/RoomFactory.kt @@ -22,6 +22,7 @@ import im.vector.matrix.android.internal.session.room.members.DefaultRoomMembers import im.vector.matrix.android.internal.session.room.members.LoadRoomMembersTask import im.vector.matrix.android.internal.session.room.members.RoomMemberExtractor import im.vector.matrix.android.internal.session.room.read.DefaultReadService +import im.vector.matrix.android.internal.session.room.read.ReadServiceListeners import im.vector.matrix.android.internal.session.room.read.SetReadMarkersTask import im.vector.matrix.android.internal.session.room.send.DefaultSendService import im.vector.matrix.android.internal.session.room.send.EventFactory @@ -49,6 +50,7 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask, val sendService = DefaultSendService(roomId, eventFactory, monarchy) val roomMembersService = DefaultRoomMembersService(roomId, monarchy, loadRoomMembersTask, taskExecutor) val readService = DefaultReadService(roomId, monarchy, roomMembersService, setReadMarkersTask, taskExecutor) + val readServiceListeners = ReadServiceListeners(roomId, monarchy) return DefaultRoom( roomId, @@ -56,6 +58,7 @@ internal class RoomFactory(private val loadRoomMembersTask: LoadRoomMembersTask, timelineService, sendService, readService, + readServiceListeners, roomMembersService ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt index 3135a1cb..5763bc5e 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/DefaultReadService.kt @@ -37,7 +37,7 @@ internal class DefaultReadService(private val roomId: String, private val taskExecutor: TaskExecutor) : ReadService { override fun markAllAsRead(callback: MatrixCallback) { - val latestEvent = getLatestEvent() + val latestEvent = monarchy.fetchCopied { EventEntity.latestEvent(it, roomId) } val params = SetReadMarkersTask.Params(roomId, fullyReadEventId = latestEvent?.eventId, readReceiptEventId = latestEvent?.eventId) setReadMarkersTask.configureWith(params).executeBy(taskExecutor) } @@ -52,10 +52,6 @@ internal class DefaultReadService(private val roomId: String, setReadMarkersTask.configureWith(params).executeBy(taskExecutor) } - private fun getLatestEvent(): EventEntity? { - return monarchy.fetchCopied { EventEntity.latestEvent(it, roomId) } - } - override fun readReceipts(): LiveData> { return monarchy.findAllMappedWithChanges( { realm -> ReadReceiptEntity.where(realm, roomId) }, @@ -68,7 +64,7 @@ internal class DefaultReadService(private val roomId: String, override fun readReceipt(eventId: String): ReadReceipt? { val readReceipt = monarchy.fetchCopied { ReadReceiptEntity.where(it, roomId).findFirst() } - ?: return null + ?: return null val roomMember = roomMembersService.getRoomMember(readReceipt.userId) return ReadReceipt(roomMember, readReceipt.eventId, readReceipt.originServerTs.toLong()) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/ReadServiceListeners.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/ReadServiceListeners.kt new file mode 100644 index 00000000..deb08ed8 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/read/ReadServiceListeners.kt @@ -0,0 +1,58 @@ +/* + * Copyright 2019 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.read + +import androidx.lifecycle.Observer +import com.zhuinden.monarchy.Monarchy +import im.vector.matrix.android.api.session.room.read.ReadService +import im.vector.matrix.android.internal.database.model.ReadReceiptEntity +import im.vector.matrix.android.internal.database.query.where + +class ReadServiceListeners(private val roomId: String, + private val monarchy: Monarchy +) { + + private val listeners = ArrayList() + private val readReceiptsLive by lazy { + monarchy.findAllManagedWithChanges { realm -> ReadReceiptEntity.where(realm, roomId) } + } + private val readReceiptLiveObserver = Observer> { listeners.forEach { it.onReadReceiptsUpdated() } } + + fun addListener(listener: ReadService.Listener) { + if (listeners.isEmpty()) { + startObservingData() + } + listeners.add(listener) + } + + fun removeListener(listener: ReadService.Listener) { + listeners.remove(listener) + if (listeners.isEmpty()) { + stopObservingData() + } + } + + private fun startObservingData() { + readReceiptsLive.observeForever(readReceiptLiveObserver) + } + + private fun stopObservingData() { + readReceiptsLive.removeObserver(readReceiptLiveObserver) + } + + +} \ No newline at end of file