|
|
|
@ -16,6 +16,7 @@
@@ -16,6 +16,7 @@
|
|
|
|
|
|
|
|
|
|
package io.element.android.libraries.matrix.impl.room |
|
|
|
|
|
|
|
|
|
import androidx.collection.lruCache |
|
|
|
|
import io.element.android.appconfig.TimelineConfig |
|
|
|
|
import io.element.android.libraries.core.coroutine.CoroutineDispatchers |
|
|
|
|
import io.element.android.libraries.matrix.api.core.RoomId |
|
|
|
@ -41,6 +42,8 @@ import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
@@ -41,6 +42,8 @@ import org.matrix.rustcomponents.sdk.TimelineEventTypeFilter
|
|
|
|
|
import timber.log.Timber |
|
|
|
|
import org.matrix.rustcomponents.sdk.RoomListService as InnerRoomListService |
|
|
|
|
|
|
|
|
|
private const val CACHE_SIZE = 16 |
|
|
|
|
|
|
|
|
|
class RustRoomFactory( |
|
|
|
|
private val sessionId: SessionId, |
|
|
|
|
private val notificationSettingsService: NotificationSettingsService, |
|
|
|
@ -55,8 +58,23 @@ class RustRoomFactory(
@@ -55,8 +58,23 @@ class RustRoomFactory(
|
|
|
|
|
private val getSessionData: suspend () -> SessionData, |
|
|
|
|
) { |
|
|
|
|
@OptIn(ExperimentalCoroutinesApi::class) |
|
|
|
|
private val createRoomDispatcher = dispatchers.io.limitedParallelism(1) |
|
|
|
|
private val dispatcher = dispatchers.io.limitedParallelism(1) |
|
|
|
|
private val mutex = Mutex() |
|
|
|
|
private var isDestroyed: Boolean = false |
|
|
|
|
|
|
|
|
|
private data class RustRoomObjects( |
|
|
|
|
val roomListItem: RoomListItem, |
|
|
|
|
val fullRoom: Room, |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
private val cache = lruCache<RoomId, RustRoomObjects>( |
|
|
|
|
maxSize = CACHE_SIZE, |
|
|
|
|
onEntryRemoved = { evicted, roomId, oldRoom, _ -> |
|
|
|
|
Timber.d("On room removed from cache: $roomId, evicted: $evicted") |
|
|
|
|
oldRoom.roomListItem.close() |
|
|
|
|
oldRoom.fullRoom.close() |
|
|
|
|
} |
|
|
|
|
) |
|
|
|
|
|
|
|
|
|
private val matrixRoomInfoMapper = MatrixRoomInfoMapper() |
|
|
|
|
|
|
|
|
@ -70,30 +88,41 @@ class RustRoomFactory(
@@ -70,30 +88,41 @@ class RustRoomFactory(
|
|
|
|
|
) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
suspend fun create(roomId: RoomId): MatrixRoom? = withContext(createRoomDispatcher) { |
|
|
|
|
var cachedPairOfRoom: Pair<RoomListItem, Room>? |
|
|
|
|
suspend fun destroy() { |
|
|
|
|
withContext(dispatcher) { |
|
|
|
|
mutex.withLock { |
|
|
|
|
Timber.d("Destroying room factory") |
|
|
|
|
cache.evictAll() |
|
|
|
|
isDestroyed = true |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
suspend fun create(roomId: RoomId): MatrixRoom? = withContext(dispatcher) { |
|
|
|
|
mutex.withLock { |
|
|
|
|
// Check if already in memory... |
|
|
|
|
cachedPairOfRoom = pairOfRoom(roomId) |
|
|
|
|
if (cachedPairOfRoom == null) { |
|
|
|
|
if (isDestroyed) { |
|
|
|
|
Timber.d("Room factory is destroyed, returning null for $roomId") |
|
|
|
|
return@withContext null |
|
|
|
|
} |
|
|
|
|
var roomObjects: RustRoomObjects? = getRoomObjects(roomId) |
|
|
|
|
if (roomObjects == null) { |
|
|
|
|
// ... otherwise, lets wait for the SS to load all rooms and check again. |
|
|
|
|
roomListService.allRooms.awaitLoaded() |
|
|
|
|
cachedPairOfRoom = pairOfRoom(roomId) |
|
|
|
|
roomObjects = getRoomObjects(roomId) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (cachedPairOfRoom == null) { |
|
|
|
|
Timber.d("No room found for $roomId") |
|
|
|
|
return@withContext null |
|
|
|
|
} |
|
|
|
|
cachedPairOfRoom?.let { (roomListItem, fullRoom) -> |
|
|
|
|
if (roomObjects == null) { |
|
|
|
|
Timber.d("No room found for $roomId, returning null") |
|
|
|
|
return@withContext null |
|
|
|
|
} |
|
|
|
|
val liveTimeline = roomObjects.fullRoom.timeline() |
|
|
|
|
RustMatrixRoom( |
|
|
|
|
sessionId = sessionId, |
|
|
|
|
isKeyBackupEnabled = isKeyBackupEnabled(), |
|
|
|
|
roomListItem = roomListItem, |
|
|
|
|
innerRoom = fullRoom, |
|
|
|
|
innerTimeline = fullRoom.timeline(), |
|
|
|
|
notificationSettingsService = notificationSettingsService, |
|
|
|
|
roomListItem = roomObjects.roomListItem, |
|
|
|
|
innerRoom = roomObjects.fullRoom, |
|
|
|
|
innerTimeline = liveTimeline, |
|
|
|
|
sessionCoroutineScope = sessionCoroutineScope, |
|
|
|
|
notificationSettingsService = notificationSettingsService, |
|
|
|
|
coroutineDispatchers = dispatchers, |
|
|
|
|
systemClock = systemClock, |
|
|
|
|
roomContentForwarder = roomContentForwarder, |
|
|
|
@ -104,20 +133,28 @@ class RustRoomFactory(
@@ -104,20 +133,28 @@ class RustRoomFactory(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private suspend fun pairOfRoom(roomId: RoomId): Pair<RoomListItem, Room>? { |
|
|
|
|
val cachedRoomListItem = innerRoomListService.roomOrNull(roomId.value) |
|
|
|
|
private suspend fun getRoomObjects(roomId: RoomId): RustRoomObjects? { |
|
|
|
|
cache[roomId]?.let { |
|
|
|
|
Timber.d("Room found in cache for $roomId") |
|
|
|
|
return it |
|
|
|
|
} |
|
|
|
|
val roomListItem = innerRoomListService.roomOrNull(roomId.value) |
|
|
|
|
if (roomListItem == null) { |
|
|
|
|
Timber.d("Room not found for $roomId") |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
val fullRoom = try { |
|
|
|
|
cachedRoomListItem?.fullRoomWithTimeline(filter = eventFilters) |
|
|
|
|
roomListItem.fullRoomWithTimeline(filter = eventFilters) |
|
|
|
|
} catch (e: RoomListException) { |
|
|
|
|
Timber.e(e, "Failed to get full room with timeline for $roomId") |
|
|
|
|
null |
|
|
|
|
return null |
|
|
|
|
} |
|
|
|
|
return if (cachedRoomListItem == null || fullRoom == null) { |
|
|
|
|
Timber.d("No room cached for $roomId") |
|
|
|
|
null |
|
|
|
|
} else { |
|
|
|
|
Timber.d("Found room cached for $roomId") |
|
|
|
|
Pair(cachedRoomListItem, fullRoom) |
|
|
|
|
Timber.d("Got full room with timeline for $roomId") |
|
|
|
|
return RustRoomObjects( |
|
|
|
|
roomListItem = roomListItem, |
|
|
|
|
fullRoom = fullRoom, |
|
|
|
|
).also { |
|
|
|
|
cache.put(roomId, it) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|