Crypto : WIP

This commit is contained in:
ganfra 2019-05-23 19:12:06 +02:00
parent bb39db3f42
commit 3519ad7c8d
14 changed files with 81 additions and 73 deletions

View File

@ -12,7 +12,7 @@ buildscript {
} }
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.4.0' classpath 'com.android.tools.build:gradle:3.4.1'
classpath 'com.google.gms:google-services:4.2.0' classpath 'com.google.gms:google-services:4.2.0'
classpath "com.airbnb.okreplay:gradle-plugin:1.4.0" classpath "com.airbnb.okreplay:gradle-plugin:1.4.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"

View File

@ -19,4 +19,4 @@ package im.vector.matrix.android
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers.Main import kotlinx.coroutines.Dispatchers.Main
internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main) internal val testCoroutineDispatchers = MatrixCoroutineDispatchers(Main, Main, Main, Main, Main)

View File

@ -22,5 +22,5 @@ enum class RoomHistoryVisibility {
@Json(name = "shared") SHARED, @Json(name = "shared") SHARED,
@Json(name = "invited") INVITED, @Json(name = "invited") INVITED,
@Json(name = "joined") JOINED, @Json(name = "joined") JOINED,
@Json(name = "word_readable") WORLD_READABLE @Json(name = "world_readable") WORLD_READABLE
} }

View File

@ -35,8 +35,10 @@ import im.vector.matrix.android.api.session.events.model.Event
import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.events.model.EventType
import im.vector.matrix.android.api.session.events.model.toModel import im.vector.matrix.android.api.session.events.model.toModel
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.Membership
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibility
import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent import im.vector.matrix.android.api.session.room.model.RoomHistoryVisibilityContent
import im.vector.matrix.android.api.session.room.model.RoomMember
import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction import im.vector.matrix.android.internal.crypto.actions.EnsureOlmSessionsForDevicesAction
import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter import im.vector.matrix.android.internal.crypto.actions.MegolmSessionDataImporter
import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction import im.vector.matrix.android.internal.crypto.actions.SetDeviceVerificationAction
@ -44,19 +46,29 @@ import im.vector.matrix.android.internal.crypto.algorithms.IMXEncrypting
import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory import im.vector.matrix.android.internal.crypto.algorithms.megolm.MXMegolmEncryptionFactory
import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory import im.vector.matrix.android.internal.crypto.algorithms.olm.MXOlmEncryptionFactory
import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup import im.vector.matrix.android.internal.crypto.keysbackup.KeysBackup
import im.vector.matrix.android.internal.crypto.model.* import im.vector.matrix.android.internal.crypto.model.ImportRoomKeysResult
import im.vector.matrix.android.internal.crypto.model.MXDeviceInfo
import im.vector.matrix.android.internal.crypto.model.MXEncryptEventContentResult
import im.vector.matrix.android.internal.crypto.model.MXOlmSessionResult
import im.vector.matrix.android.internal.crypto.model.MXUsersDevicesMap
import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent import im.vector.matrix.android.internal.crypto.model.event.RoomKeyContent
import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse import im.vector.matrix.android.internal.crypto.model.rest.DevicesListResponse
import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody import im.vector.matrix.android.internal.crypto.model.rest.RoomKeyRequestBody
import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository import im.vector.matrix.android.internal.crypto.repository.WarnOnUnknownDeviceRepository
import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore import im.vector.matrix.android.internal.crypto.store.IMXCryptoStore
import im.vector.matrix.android.internal.crypto.tasks.* import im.vector.matrix.android.internal.crypto.tasks.ClaimOneTimeKeysForUsersDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.DeleteDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.GetDevicesTask
import im.vector.matrix.android.internal.crypto.tasks.GetKeyChangesTask
import im.vector.matrix.android.internal.crypto.tasks.SendToDeviceTask
import im.vector.matrix.android.internal.crypto.tasks.SetDeviceNameTask
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService import im.vector.matrix.android.internal.crypto.verification.DefaultSasVerificationService
import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.session.room.members.RoomMembers
import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import org.matrix.olm.OlmManager import org.matrix.olm.OlmManager
import timber.log.Timber import timber.log.Timber
@ -230,15 +242,8 @@ internal class CryptoManager(
* devices. * devices.
* *
* @param isInitialSync true if it starts from an initial sync * @param isInitialSync true if it starts from an initial sync
* @param aCallback the asynchronous callback
*/ */
fun start(isInitialSync: Boolean, aCallback: MatrixCallback<Unit>?) { fun start(isInitialSync: Boolean) {
synchronized(mInitializationCallbacks) {
if (null != aCallback && mInitializationCallbacks.indexOf(aCallback) < 0) {
mInitializationCallbacks.add(aCallback)
}
}
if (mIsStarting) { if (mIsStarting) {
return return
} }
@ -260,11 +265,11 @@ internal class CryptoManager(
uploadDeviceKeys(object : MatrixCallback<KeysUploadResponse> { uploadDeviceKeys(object : MatrixCallback<KeysUploadResponse> {
private fun onError() { private fun onError() {
Handler().postDelayed({ Handler().postDelayed({
if (!isStarted()) { if (!isStarted()) {
mIsStarting = false mIsStarting = false
start(isInitialSync, null) start(isInitialSync)
} }
}, 1000) }, 1000)
} }
override fun onSuccess(data: KeysUploadResponse) { override fun onSuccess(data: KeysUploadResponse) {
@ -290,13 +295,6 @@ internal class CryptoManager(
mKeysBackup.checkAndStartKeysBackup() mKeysBackup.checkAndStartKeysBackup()
synchronized(mInitializationCallbacks) {
for (callback in mInitializationCallbacks) {
callback.onSuccess(Unit)
}
mInitializationCallbacks.clear()
}
if (isInitialSync) { if (isInitialSync) {
// refresh the devices list for each known room members // refresh the devices list for each known room members
deviceListManager.invalidateAllDeviceLists() deviceListManager.invalidateAllDeviceLists()
@ -605,17 +603,7 @@ internal class CryptoManager(
if (!isStarted()) { if (!isStarted()) {
Timber.v("## encryptEventContent() : wait after e2e init") Timber.v("## encryptEventContent() : wait after e2e init")
start(false, object : MatrixCallback<Unit> { start(false)
override fun onSuccess(data: Unit) {
encryptEventContent(eventContent, eventType, room, callback)
}
override fun onFailure(failure: Throwable) {
Timber.e(failure, "## encryptEventContent() : onNetworkError while waiting to start e2e")
callback.onFailure(failure)
}
})
return return
} }
@ -669,11 +657,11 @@ internal class CryptoManager(
} else { } else {
val algorithm = room.encryptionAlgorithm() val algorithm = room.encryptionAlgorithm()
val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON, val reason = String.format(MXCryptoError.UNABLE_TO_ENCRYPT_REASON,
algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON) algorithm ?: MXCryptoError.NO_MORE_ALGORITHM_REASON)
Timber.e("## encryptEventContent() : $reason") Timber.e("## encryptEventContent() : $reason")
callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE, callback.onFailure(Failure.CryptoError(MXCryptoError(MXCryptoError.UNABLE_TO_ENCRYPT_ERROR_CODE,
MXCryptoError.UNABLE_TO_ENCRYPT, reason))) MXCryptoError.UNABLE_TO_ENCRYPT, reason)))
} }
} }
@ -703,7 +691,7 @@ internal class CryptoManager(
val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String) val reason = String.format(MXCryptoError.UNABLE_TO_DECRYPT_REASON, event.eventId, eventContent["algorithm"] as String)
Timber.e("## decryptEvent() : $reason") Timber.e("## decryptEvent() : $reason")
exceptions.add(MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE, exceptions.add(MXDecryptionException(MXCryptoError(MXCryptoError.UNABLE_TO_DECRYPT_ERROR_CODE,
MXCryptoError.UNABLE_TO_DECRYPT, reason))) MXCryptoError.UNABLE_TO_DECRYPT, reason)))
} else { } else {
try { try {
result = alg.decryptEvent(event, timeline) result = alg.decryptEvent(event, timeline)
@ -796,23 +784,15 @@ internal class CryptoManager(
// No encrypting in this room // No encrypting in this room
return return
} }
event.stateKey?.let { userId ->
val userId = event.stateKey!! val roomMember: RoomMember? = event.content.toModel()
val membership = roomMember?.membership
/* FIXME
val room = mRoomService.getRoom(roomId)
val roomMember = room?.getRoomMember(userId)
if (null != roomMember) {
val membership = roomMember.membership
if (membership == Membership.JOIN) { if (membership == Membership.JOIN) {
// make sure we are tracking the deviceList for this user. // make sure we are tracking the deviceList for this user.
deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
} else if (membership == Membership.INVITE } else if (membership == Membership.INVITE
&& shouldEncryptForInvitedMembers(roomId) && shouldEncryptForInvitedMembers(roomId)
&& mCryptoConfig.mEnableEncryptionForInvitedMembers) { && mCryptoConfig.mEnableEncryptionForInvitedMembers) {
// track the deviceList for this invited user. // track the deviceList for this invited user.
// Caution: there's a big edge case here in that federated servers do not // Caution: there's a big edge case here in that federated servers do not
// know what other servers are in the room at the time they've been invited. // know what other servers are in the room at the time they've been invited.
@ -821,7 +801,6 @@ internal class CryptoManager(
deviceListManager.startTrackingDeviceList(Arrays.asList(userId)) deviceListManager.startTrackingDeviceList(Arrays.asList(userId))
} }
} }
*/
} }
private fun onRoomHistoryVisibilityEvent(roomId: String, event: Event) { private fun onRoomHistoryVisibilityEvent(roomId: String, event: Event) {
@ -851,6 +830,7 @@ internal class CryptoManager(
// same one as used in login. // same one as used in login.
mUploadKeysTask mUploadKeysTask
.configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId)) .configureWith(UploadKeysTask.Params(getMyDevice().toDeviceKeys(), null, getMyDevice().deviceId))
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(callback) .dispatchTo(callback)
.executeBy(mTaskExecutor) .executeBy(mTaskExecutor)
} }
@ -980,7 +960,7 @@ internal class CryptoManager(
// trigger an an unknown devices exception // trigger an an unknown devices exception
callback.onFailure( callback.onFailure(
Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE, Failure.CryptoError(MXCryptoError(MXCryptoError.UNKNOWN_DEVICES_CODE,
MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices))) MXCryptoError.UNABLE_TO_ENCRYPT, MXCryptoError.UNKNOWN_DEVICES_REASON, unknownDevices)))
} }
} }

View File

@ -23,6 +23,7 @@ import im.vector.matrix.android.internal.crypto.model.rest.KeysUploadResponse
import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask import im.vector.matrix.android.internal.crypto.tasks.UploadKeysTask
import im.vector.matrix.android.internal.di.MoshiProvider import im.vector.matrix.android.internal.di.MoshiProvider
import im.vector.matrix.android.internal.task.TaskExecutor import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.task.TaskThread
import im.vector.matrix.android.internal.task.configureWith import im.vector.matrix.android.internal.task.configureWith
import org.matrix.olm.OlmAccount import org.matrix.olm.OlmAccount
import timber.log.Timber import timber.log.Timber
@ -95,6 +96,7 @@ internal class OneTimeKeysUploader(
// ask the server how many keys we have // ask the server how many keys we have
mUploadKeysTask mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, null, mCredentials.deviceId!!)) .configureWith(UploadKeysTask.Params(null, null, mCredentials.deviceId!!))
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(object : MatrixCallback<KeysUploadResponse> { .dispatchTo(object : MatrixCallback<KeysUploadResponse> {
override fun onSuccess(data: KeysUploadResponse) { override fun onSuccess(data: KeysUploadResponse) {
@ -192,6 +194,7 @@ internal class OneTimeKeysUploader(
// same one as used in login. // same one as used in login.
mUploadKeysTask mUploadKeysTask
.configureWith(UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!)) .configureWith(UploadKeysTask.Params(null, oneTimeJson, mCredentials.deviceId!!))
.executeOn(TaskThread.ENCRYPTION)
.dispatchTo(object : MatrixCallback<KeysUploadResponse> { .dispatchTo(object : MatrixCallback<KeysUploadResponse> {
override fun onSuccess(data: KeysUploadResponse) { override fun onSuccess(data: KeysUploadResponse) {
mLastPublishedOneTimeKeys = oneTimeKeys mLastPublishedOneTimeKeys = oneTimeKeys

View File

@ -21,6 +21,7 @@ import androidx.work.Worker
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.squareup.moshi.JsonClass import com.squareup.moshi.JsonClass
import com.zhuinden.monarchy.Monarchy import com.zhuinden.monarchy.Monarchy
import im.vector.matrix.android.api.MatrixCallback
import im.vector.matrix.android.internal.crypto.CryptoManager import im.vector.matrix.android.internal.crypto.CryptoManager
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.EventEntity import im.vector.matrix.android.internal.database.model.EventEntity
@ -50,8 +51,8 @@ internal class EnableEncryptionWorker(context: Context,
override fun doWork(): Result { override fun doWork(): Result {
val params = WorkerParamsFactory.fromData<EnableEncryptionWorker.Params>(inputData) val params = WorkerParamsFactory.fromData<Params>(inputData)
?: return Result.failure() ?: return Result.failure()
val events = monarchy.fetchAllMappedSync( val events = monarchy.fetchAllMappedSync(
@ -62,9 +63,17 @@ internal class EnableEncryptionWorker(context: Context,
events.forEach { events.forEach {
val roomId = it.roomId!! val roomId = it.roomId!!
val callback = object : MatrixCallback<Boolean> {
override fun onSuccess(data: Boolean) {
super.onSuccess(data)
}
}
loadRoomMembersTask loadRoomMembersTask
.configureWith(LoadRoomMembersTask.Params(roomId)) .configureWith(LoadRoomMembersTask.Params(roomId))
.executeOn(TaskThread.CALLER) .executeOn(TaskThread.ENCRYPTION)
.dispatchTo(callback)
.executeBy(taskExecutor) .executeBy(taskExecutor)
var userIds: List<String> = emptyList() var userIds: List<String> = emptyList()
@ -72,7 +81,7 @@ internal class EnableEncryptionWorker(context: Context,
monarchy.doWithRealm { realm -> monarchy.doWithRealm { realm ->
// Check whether the event content must be encrypted for the invited members. // Check whether the event content must be encrypted for the invited members.
val encryptForInvitedMembers = cryptoManager.isEncryptionEnabledForInvitedUser() val encryptForInvitedMembers = cryptoManager.isEncryptionEnabledForInvitedUser()
&& cryptoManager.shouldEncryptForInvitedMembers(roomId) && cryptoManager.shouldEncryptForInvitedMembers(roomId)
userIds = if (encryptForInvitedMembers) { userIds = if (encryptForInvitedMembers) {

View File

@ -21,7 +21,9 @@ import im.vector.matrix.android.internal.task.TaskExecutor
import im.vector.matrix.android.internal.util.BackgroundDetectionObserver import im.vector.matrix.android.internal.util.BackgroundDetectionObserver
import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.asCoroutineDispatcher
import org.koin.dsl.module.module import org.koin.dsl.module.module
import java.util.concurrent.Executors
class MatrixModule(private val context: Context) { class MatrixModule(private val context: Context) {
@ -33,7 +35,12 @@ class MatrixModule(private val context: Context) {
} }
single { single {
MatrixCoroutineDispatchers(io = Dispatchers.IO, computation = Dispatchers.IO, main = Dispatchers.Main) MatrixCoroutineDispatchers(io = Dispatchers.IO,
computation = Dispatchers.IO,
main = Dispatchers.Main,
encryption = Executors.newSingleThreadExecutor().asCoroutineDispatcher(),
decryption = Executors.newSingleThreadExecutor().asCoroutineDispatcher()
)
} }
single { single {

View File

@ -103,18 +103,17 @@ internal class DefaultSession(override val sessionParams: SessionParams) : Sessi
val contentModule = ContentModule().definition val contentModule = ContentModule().definition
val cryptoModule = CryptoModule().definition val cryptoModule = CryptoModule().definition
MatrixKoinHolder.instance.loadModules(listOf(sessionModule, MatrixKoinHolder.instance.loadModules(listOf(sessionModule,
syncModule, syncModule,
roomModule, roomModule,
groupModule, groupModule,
userModule, userModule,
signOutModule, signOutModule,
contentModule, contentModule,
cryptoModule)) cryptoModule))
scope = getKoin().getOrCreateScope(SCOPE) scope = getKoin().getOrCreateScope(SCOPE)
if (!monarchy.isMonarchyThreadOpen) { if (!monarchy.isMonarchyThreadOpen) {
monarchy.openManually() monarchy.openManually()
} }
cryptoService.start(false, null)
liveEntityUpdaters.forEach { it.start() } liveEntityUpdaters.forEach { it.start() }
} }

View File

@ -56,7 +56,7 @@ internal class SyncModule {
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {
SyncResponseHandler(get(), get(), get(), get()) SyncResponseHandler(get(), get(), get(), get(), get())
} }
scope(DefaultSession.SCOPE) { scope(DefaultSession.SCOPE) {

View File

@ -17,6 +17,7 @@
package im.vector.matrix.android.internal.session.sync package im.vector.matrix.android.internal.session.sync
import arrow.core.Try import arrow.core.Try
import im.vector.matrix.android.internal.crypto.CryptoManager
import im.vector.matrix.android.internal.session.sync.model.SyncResponse import im.vector.matrix.android.internal.session.sync.model.SyncResponse
import timber.log.Timber import timber.log.Timber
import kotlin.system.measureTimeMillis import kotlin.system.measureTimeMillis
@ -24,7 +25,8 @@ import kotlin.system.measureTimeMillis
internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler, internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
private val userAccountDataSyncHandler: UserAccountDataSyncHandler, private val userAccountDataSyncHandler: UserAccountDataSyncHandler,
private val groupSyncHandler: GroupSyncHandler, private val groupSyncHandler: GroupSyncHandler,
private val cryptoSyncHandler: CryptoSyncHandler) { private val cryptoSyncHandler: CryptoSyncHandler,
private val cryptoManager: CryptoManager) {
fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> { fun handleResponse(syncResponse: SyncResponse, fromToken: String?, isCatchingUp: Boolean): Try<SyncResponse> {
return Try { return Try {
@ -44,9 +46,10 @@ internal class SyncResponseHandler(private val roomSyncHandler: RoomSyncHandler,
if (syncResponse.accountData != null) { if (syncResponse.accountData != null) {
userAccountDataSyncHandler.handle(syncResponse.accountData) userAccountDataSyncHandler.handle(syncResponse.accountData)
} }
cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp) cryptoSyncHandler.onSyncCompleted(syncResponse, fromToken, isCatchingUp)
} }
val isInitialSync = fromToken == null
cryptoManager.start(isInitialSync)
Timber.v("Finish handling sync in $measure ms") Timber.v("Finish handling sync in $measure ms")
syncResponse syncResponse
} }

View File

@ -68,6 +68,8 @@ internal class TaskExecutor(private val coroutineDispatchers: MatrixCoroutineDis
TaskThread.COMPUTATION -> coroutineDispatchers.computation TaskThread.COMPUTATION -> coroutineDispatchers.computation
TaskThread.IO -> coroutineDispatchers.io TaskThread.IO -> coroutineDispatchers.io
TaskThread.CALLER -> EmptyCoroutineContext TaskThread.CALLER -> EmptyCoroutineContext
TaskThread.ENCRYPTION -> coroutineDispatchers.encryption
TaskThread.DECRYPTION -> coroutineDispatchers.decryption
} }

View File

@ -20,5 +20,7 @@ internal enum class TaskThread {
MAIN, MAIN,
COMPUTATION, COMPUTATION,
IO, IO,
CALLER CALLER,
ENCRYPTION,
DECRYPTION
} }

View File

@ -21,5 +21,7 @@ import kotlinx.coroutines.CoroutineDispatcher
internal data class MatrixCoroutineDispatchers( internal data class MatrixCoroutineDispatchers(
val io: CoroutineDispatcher, val io: CoroutineDispatcher,
val computation: CoroutineDispatcher, val computation: CoroutineDispatcher,
val main: CoroutineDispatcher val main: CoroutineDispatcher,
val decryption: CoroutineDispatcher,
val encryption: CoroutineDispatcher
) )

View File

@ -30,6 +30,7 @@ import im.vector.matrix.android.api.session.room.model.message.*
import im.vector.matrix.android.api.session.room.timeline.Timeline import im.vector.matrix.android.api.session.room.timeline.Timeline
import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.matrix.android.api.session.room.timeline.TimelineEvent
import im.vector.riotredesign.core.epoxy.LoadingItemModel_ import im.vector.riotredesign.core.epoxy.LoadingItemModel_
import im.vector.riotredesign.core.epoxy.loadingItem
import im.vector.riotredesign.core.extensions.localDateTime import im.vector.riotredesign.core.extensions.localDateTime
import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory import im.vector.riotredesign.features.home.room.detail.timeline.factory.TimelineItemFactory
import im.vector.riotredesign.features.home.room.detail.timeline.helper.* import im.vector.riotredesign.features.home.room.detail.timeline.helper.*