Suspend api: continue moving away from callback

This commit is contained in:
ganfra 2022-04-06 19:02:45 +02:00
parent 9c6fccab1d
commit ed84e38a9b
14 changed files with 103 additions and 121 deletions

View File

@ -64,7 +64,7 @@ interface CryptoService {
fun setDeviceVerification(trustLevel: DeviceTrustLevel, userId: String, deviceId: String) fun setDeviceVerification(trustLevel: DeviceTrustLevel, userId: String, deviceId: String)
fun getUserDevices(userId: String): MutableList<CryptoDeviceInfo> suspend fun getUserDevices(userId: String): MutableList<CryptoDeviceInfo>
fun getMyDevice(): CryptoDeviceInfo fun getMyDevice(): CryptoDeviceInfo
@ -84,7 +84,7 @@ interface CryptoService {
fun setRoomBlacklistUnverifiedDevices(roomId: String) fun setRoomBlacklistUnverifiedDevices(roomId: String)
fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? suspend fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo?
fun reRequestRoomKeyForEvent(event: Event) fun reRequestRoomKeyForEvent(event: Event)
@ -92,7 +92,7 @@ interface CryptoService {
fun removeRoomKeysRequestListener(listener: GossipingRequestListener) fun removeRoomKeysRequestListener(listener: GossipingRequestListener)
fun fetchDevicesList(callback: MatrixCallback<DevicesListResponse>) suspend fun fetchDevicesList(): List<DeviceInfo>
fun getMyDevicesInfo(): List<DeviceInfo> fun getMyDevicesInfo(): List<DeviceInfo>
@ -112,7 +112,7 @@ interface CryptoService {
fun discardOutboundSession(roomId: String) fun discardOutboundSession(roomId: String)
@Throws(MXCryptoError::class) @Throws(MXCryptoError::class)
fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult
fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback<MXEventDecryptionResult>) fun decryptEventAsync(event: Event, timeline: String, callback: MatrixCallback<MXEventDecryptionResult>)
@ -122,7 +122,7 @@ interface CryptoService {
suspend fun downloadKeys(userIds: List<String>, forceDownload: Boolean = false): MXUsersDevicesMap<CryptoDeviceInfo> suspend fun downloadKeys(userIds: List<String>, forceDownload: Boolean = false): MXUsersDevicesMap<CryptoDeviceInfo>
fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo>
fun getLiveCryptoDeviceInfo(userId: String): Flow<List<CryptoDeviceInfo>> fun getLiveCryptoDeviceInfo(userId: String): Flow<List<CryptoDeviceInfo>>

View File

@ -66,7 +66,6 @@ import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyContent
import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent import org.matrix.android.sdk.internal.crypto.model.event.RoomKeyWithHeldContent
import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent import org.matrix.android.sdk.internal.crypto.model.event.SecretSendEventContent
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse
import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent import org.matrix.android.sdk.internal.crypto.model.rest.ForwardedRoomKeyContent
import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository import org.matrix.android.sdk.internal.crypto.repository.WarnOnUnknownDeviceRepository
import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore import org.matrix.android.sdk.internal.crypto.store.IMXCryptoStore
@ -224,23 +223,12 @@ internal class DefaultCryptoService @Inject constructor(
return runBlocking { olmMachine.ownDevice() } return runBlocking { olmMachine.ownDevice() }
} }
override fun fetchDevicesList(callback: MatrixCallback<DevicesListResponse>) { override suspend fun fetchDevicesList(): List<DeviceInfo> {
getDevicesTask val devicesList = tryOrNull {
.configureWith { getDevicesTask.execute(Unit).devices
// this.executionThread = TaskThread.CRYPTO }.orEmpty()
this.callback = object : MatrixCallback<DevicesListResponse> { cryptoStore.saveMyDevicesInfo(devicesList)
override fun onFailure(failure: Throwable) { return devicesList
callback.onFailure(failure)
}
override fun onSuccess(data: DevicesListResponse) {
// Save in local DB
cryptoStore.saveMyDevicesInfo(data.devices.orEmpty())
callback.onSuccess(data)
}
}
}
.executeBy(taskExecutor)
} }
override fun getLiveMyDevicesInfo(): LiveData<List<DeviceInfo>> { override fun getLiveMyDevicesInfo(): LiveData<List<DeviceInfo>> {
@ -301,10 +289,9 @@ internal class DefaultCryptoService @Inject constructor(
*/ */
fun start() { fun start() {
internalStart() internalStart()
// Just update
fetchDevicesList(NoOpMatrixCallback())
cryptoCoroutineScope.launch(coroutineDispatchers.crypto) { cryptoCoroutineScope.launch(coroutineDispatchers.crypto) {
// Just update
fetchDevicesList()
cryptoStore.tidyUpDataBase() cryptoStore.tidyUpDataBase()
} }
} }
@ -412,20 +399,13 @@ internal class DefaultCryptoService @Inject constructor(
* @param userId the user id * @param userId the user id
* @param deviceId the device id * @param deviceId the device id
*/ */
override fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? { override suspend fun getDeviceInfo(userId: String, deviceId: String?): CryptoDeviceInfo? {
return if (userId.isNotEmpty() && !deviceId.isNullOrEmpty()) { if (userId.isEmpty() || deviceId.isNullOrEmpty()) return null
runBlocking { return olmMachine.getCryptoDeviceInfo(userId, deviceId)
this@DefaultCryptoService.olmMachine.getCryptoDeviceInfo(userId, deviceId)
}
} else {
null
}
} }
override fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> { override suspend fun getCryptoDeviceInfo(userId: String): List<CryptoDeviceInfo> {
return runBlocking { return olmMachine.getCryptoDeviceInfo(userId)
this@DefaultCryptoService.olmMachine.getCryptoDeviceInfo(userId)
}
} }
override fun getLiveCryptoDeviceInfo(userId: String): Flow<List<CryptoDeviceInfo>> { override fun getLiveCryptoDeviceInfo(userId: String): Flow<List<CryptoDeviceInfo>> {
@ -503,7 +483,7 @@ internal class DefaultCryptoService @Inject constructor(
/** /**
* @return the stored device keys for a user. * @return the stored device keys for a user.
*/ */
override fun getUserDevices(userId: String): MutableList<CryptoDeviceInfo> { override suspend fun getUserDevices(userId: String): MutableList<CryptoDeviceInfo> {
return this.getCryptoDeviceInfo(userId).toMutableList() return this.getCryptoDeviceInfo(userId).toMutableList()
} }
@ -577,10 +557,8 @@ internal class DefaultCryptoService @Inject constructor(
* @return the MXEventDecryptionResult data, or throw in case of error * @return the MXEventDecryptionResult data, or throw in case of error
*/ */
@Throws(MXCryptoError::class) @Throws(MXCryptoError::class)
override fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult { override suspend fun decryptEvent(event: Event, timeline: String): MXEventDecryptionResult {
return runBlocking { return olmMachine.decryptRoomEvent(event)
olmMachine.decryptRoomEvent(event)
}
} }
/** /**

View File

@ -362,11 +362,11 @@ internal class OlmMachine(
suspend fun decryptRoomEvent(event: Event): MXEventDecryptionResult = suspend fun decryptRoomEvent(event: Event): MXEventDecryptionResult =
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java) val adapter = MoshiProvider.providesMoshi().adapter(Event::class.java)
val serializedEvent = adapter.toJson(event)
try { try {
if (event.roomId.isNullOrBlank()) { if (event.roomId.isNullOrBlank()) {
throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON) throw MXCryptoError.Base(MXCryptoError.ErrorType.MISSING_FIELDS, MXCryptoError.MISSING_FIELDS_REASON)
} }
val serializedEvent = adapter.toJson(event)
val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId) val decrypted = inner.decryptRoomEvent(serializedEvent, event.roomId)
val deserializationAdapter = val deserializationAdapter =

View File

@ -156,7 +156,7 @@ internal class DefaultFetchThreadTimelineTask @Inject constructor(
* Invoke the event decryption mechanism for a specific event * Invoke the event decryption mechanism for a specific event
*/ */
private fun decryptIfNeeded(event: Event, roomId: String) { private suspend fun decryptIfNeeded(event: Event, roomId: String) {
try { try {
// Event from sync does not have roomId, so add it to the event first // Event from sync does not have roomId, so add it to the event first
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "") val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "")

View File

@ -43,6 +43,7 @@ internal class RealmSendingEventsDataSource(
private var roomEntity: RoomEntity? = null private var roomEntity: RoomEntity? = null
private var sendingTimelineEvents: RealmList<TimelineEventEntity>? = null private var sendingTimelineEvents: RealmList<TimelineEventEntity>? = null
private var frozenSendingTimelineEvents: RealmList<TimelineEventEntity>? = null private var frozenSendingTimelineEvents: RealmList<TimelineEventEntity>? = null
private val builtEvents = ArrayList<TimelineEvent>()
private val sendingTimelineEventsListener = RealmChangeListener<RealmList<TimelineEventEntity>> { events -> private val sendingTimelineEventsListener = RealmChangeListener<RealmList<TimelineEventEntity>> { events ->
uiEchoManager.onSentEventsInDatabase(events.map { it.eventId }) uiEchoManager.onSentEventsInDatabase(events.map { it.eventId })

View File

@ -17,6 +17,7 @@ package org.matrix.android.sdk.internal.session.room.timeline
import io.realm.Realm import io.realm.Realm
import io.realm.RealmConfiguration import io.realm.RealmConfiguration
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.session.crypto.CryptoService import org.matrix.android.sdk.api.session.crypto.CryptoService
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -126,7 +127,9 @@ internal class TimelineEventDecryptor @Inject constructor(
return return
} }
try { try {
val result = cryptoService.decryptEvent(request.event, timelineId) val result = runBlocking {
cryptoService.decryptEvent(request.event, timelineId)
}
Timber.v("Successfully decrypted event ${event.eventId}") Timber.v("Successfully decrypted event ${event.eventId}")
realm.executeTransaction { realm.executeTransaction {
val eventId = event.eventId ?: return@executeTransaction val eventId = event.eventId ?: return@executeTransaction

View File

@ -19,6 +19,7 @@ package org.matrix.android.sdk.internal.session.sync.handler.room
import dagger.Lazy import dagger.Lazy
import io.realm.Realm import io.realm.Realm
import io.realm.kotlin.createObject import io.realm.kotlin.createObject
import kotlinx.coroutines.runBlocking
import org.matrix.android.sdk.api.session.crypto.MXCryptoError import org.matrix.android.sdk.api.session.crypto.MXCryptoError
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
@ -343,7 +344,7 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
return roomEntity return roomEntity
} }
private suspend fun handleTimelineEvents(realm: Realm, private fun handleTimelineEvents(realm: Realm,
roomId: String, roomId: String,
roomEntity: RoomEntity, roomEntity: RoomEntity,
eventList: List<Event>, eventList: List<Event>,
@ -458,7 +459,9 @@ internal class RoomSyncHandler @Inject constructor(private val readReceiptHandle
private fun decryptIfNeeded(event: Event, roomId: String) { private fun decryptIfNeeded(event: Event, roomId: String) {
try { try {
// Event from sync does not have roomId, so add it to the event first // Event from sync does not have roomId, so add it to the event first
val result = cryptoService.decryptEvent(event.copy(roomId = roomId), "") val result = runBlocking {
cryptoService.decryptEvent(event.copy(roomId = roomId), "")
}
event.mxDecryptionResult = OlmDecryptionResult( event.mxDecryptionResult = OlmDecryptionResult(
payload = result.clearEvent, payload = result.clearEvent,
senderKey = result.senderCurve25519Key, senderKey = result.senderCurve25519Key,

View File

@ -131,28 +131,29 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
session.cryptoService().verificationService().getExistingTransaction(initialState.otherUserId, it) as? QrCodeVerificationTransaction session.cryptoService().verificationService().getExistingTransaction(initialState.otherUserId, it) as? QrCodeVerificationTransaction
} }
val hasAnyOtherSession = session.cryptoService() viewModelScope.launch {
.getCryptoDeviceInfo(session.myUserId)
.any {
it.deviceId != session.sessionParams.deviceId
}
setState { val hasAnyOtherSession = session.cryptoService()
copy( .getCryptoDeviceInfo(session.myUserId)
otherUserMxItem = userItem?.toMatrixItem(), .any {
sasTransactionState = sasTx?.state, it.deviceId != session.sessionParams.deviceId
qrTransactionState = qrTx?.state, }
transactionId = pr?.transactionId ?: initialState.verificationId,
pendingRequest = if (pr != null) Success(pr) else Uninitialized,
isMe = initialState.otherUserId == session.myUserId,
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup(),
hasAnyOtherSession = hasAnyOtherSession
)
}
if (autoReady) { setState {
viewModelScope.launch { copy(
otherUserMxItem = userItem?.toMatrixItem(),
sasTransactionState = sasTx?.state,
qrTransactionState = qrTx?.state,
transactionId = pr?.transactionId ?: initialState.verificationId,
pendingRequest = if (pr != null) Success(pr) else Uninitialized,
isMe = initialState.otherUserId == session.myUserId,
currentDeviceCanCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
quadSContainsSecrets = session.sharedSecretStorageService.isRecoverySetup(),
hasAnyOtherSession = hasAnyOtherSession
)
}
if (autoReady) {
// TODO, can I be here in DM mode? in this case should test if roomID is null? // TODO, can I be here in DM mode? in this case should test if roomID is null?
session.cryptoService().verificationService() session.cryptoService().verificationService()
.readyPendingVerification( .readyPendingVerification(
@ -480,7 +481,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
} }
} }
private fun handleTransactionUpdate(state: VerificationBottomSheetViewState, tx: VerificationTransaction){ private fun handleTransactionUpdate(state: VerificationBottomSheetViewState, tx: VerificationTransaction) {
viewModelScope.launch { viewModelScope.launch {
if (state.selfVerificationMode && state.transactionId == null) { if (state.selfVerificationMode && state.transactionId == null) {
// is this an incoming with that user // is this an incoming with that user

View File

@ -32,10 +32,11 @@ import im.vector.app.core.platform.VectorViewModelAction
import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.settings.VectorPreferences
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.sample import kotlinx.coroutines.flow.sample
import org.matrix.android.sdk.api.NoOpMatrixCallback import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.util.MatrixItem import org.matrix.android.sdk.api.util.MatrixItem
@ -58,7 +59,7 @@ data class DeviceDetectionInfo(
class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState, class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted initialState: UnknownDevicesState,
session: Session, session: Session,
private val vectorPreferences: VectorPreferences) : private val vectorPreferences: VectorPreferences) :
VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) { VectorViewModel<UnknownDevicesState, UnknownDeviceDetectorSharedViewModel.Action, EmptyViewEvents>(initialState) {
sealed class Action : VectorViewModelAction { sealed class Action : VectorViewModelAction {
data class IgnoreDevice(val deviceIds: List<String>) : Action() data class IgnoreDevice(val deviceIds: List<String>) : Action()
@ -75,12 +76,6 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
init { init {
val currentSessionTs = session.cryptoService().getCryptoDeviceInfo(session.myUserId)
.firstOrNull { it.deviceId == session.sessionParams.deviceId }
?.firstTimeSeenLocalTs
?: System.currentTimeMillis()
Timber.v("## Detector - Current Session first time seen $currentSessionTs")
ignoredDeviceList.addAll( ignoredDeviceList.addAll(
vectorPreferences.getUnknownDeviceDismissedList().also { vectorPreferences.getUnknownDeviceDismissedList().also {
Timber.v("## Detector - Remembered ignored list $it") Timber.v("## Detector - Remembered ignored list $it")
@ -90,10 +85,12 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
combine( combine(
session.flow().liveUserCryptoDevices(session.myUserId), session.flow().liveUserCryptoDevices(session.myUserId),
session.flow().liveMyDevicesInfo(), session.flow().liveMyDevicesInfo(),
session.flow().liveCrossSigningPrivateKeys() session.flow().liveCrossSigningPrivateKeys(),
) { cryptoList, infoList, pInfo -> session.firstTimeDeviceSeen(),
) { cryptoList, infoList, pInfo, firstTimeDeviceSeen ->
// Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}") // Timber.v("## Detector trigger ${cryptoList.map { "${it.deviceId} ${it.trustLevel}" }}")
// Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}") // Timber.v("## Detector trigger canCrossSign ${pInfo.get().selfSigned != null}")
Timber.v("## Detector - Current Session first time seen $firstTimeDeviceSeen")
infoList infoList
.filter { info -> .filter { info ->
// filter verified session, by checking the crypto device info // filter verified session, by checking the crypto device info
@ -106,7 +103,7 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
val deviceKnownSince = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }?.firstTimeSeenLocalTs ?: 0 val deviceKnownSince = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }?.firstTimeSeenLocalTs ?: 0
DeviceDetectionInfo( DeviceDetectionInfo(
deviceInfo, deviceInfo,
deviceKnownSince > currentSessionTs + 60_000, // short window to avoid false positive, deviceKnownSince > firstTimeDeviceSeen + 60_000, // short window to avoid false positive,
pInfo.getOrNull()?.selfSigned != null // adding this to pass distinct when cross sign change pInfo.getOrNull()?.selfSigned != null // adding this to pass distinct when cross sign change
) )
} }
@ -125,12 +122,14 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
.sample(5_000) .sample(5_000)
.onEach { .onEach {
// If we have a new crypto device change, we might want to trigger refresh of device info // If we have a new crypto device change, we might want to trigger refresh of device info
session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) session.cryptoService().fetchDevicesList()
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)
// trigger a refresh of lastSeen / last Ip // trigger a refresh of lastSeen / last Ip
session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) viewModelScope.launch {
session.cryptoService().fetchDevicesList()
}
} }
override fun handle(action: Action) { override fun handle(action: Action) {
@ -154,4 +153,13 @@ class UnknownDeviceDetectorSharedViewModel @AssistedInject constructor(@Assisted
vectorPreferences.storeUnknownDeviceDismissedList(ignoredDeviceList) vectorPreferences.storeUnknownDeviceDismissedList(ignoredDeviceList)
super.onCleared() super.onCleared()
} }
private fun Session.firstTimeDeviceSeen() = flow {
val value = cryptoService().getCryptoDeviceInfo(myUserId)
.firstOrNull { it.deviceId == sessionParams.deviceId }
?.firstTimeSeenLocalTs
?: System.currentTimeMillis()
emit(value)
}
} }

View File

@ -164,11 +164,12 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
onEach(MessageActionState::timelineEvent, MessageActionState::actionPermissions) { timelineEvent, permissions -> onEach(MessageActionState::timelineEvent, MessageActionState::actionPermissions) { timelineEvent, permissions ->
val nonNullTimelineEvent = timelineEvent() ?: return@onEach val nonNullTimelineEvent = timelineEvent() ?: return@onEach
eventIdFlow.tryEmit(nonNullTimelineEvent.eventId) eventIdFlow.tryEmit(nonNullTimelineEvent.eventId)
val events = actionsForEvent(nonNullTimelineEvent, permissions)
setState { setState {
copy( copy(
eventId = nonNullTimelineEvent.eventId, eventId = nonNullTimelineEvent.eventId,
messageBody = computeMessageBody(nonNullTimelineEvent), messageBody = computeMessageBody(nonNullTimelineEvent),
actions = actionsForEvent(nonNullTimelineEvent, permissions) actions = events
) )
} }
} }
@ -246,7 +247,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
} }
} }
private fun actionsForEvent(timelineEvent: TimelineEvent, actionPermissions: ActionPermissions): List<EventSharedAction> { private suspend fun actionsForEvent(timelineEvent: TimelineEvent, actionPermissions: ActionPermissions): List<EventSharedAction> {
val messageContent = timelineEvent.getLastMessageContent() val messageContent = timelineEvent.getLastMessageContent()
val msgType = messageContent?.msgType val msgType = messageContent?.msgType
@ -317,7 +318,7 @@ class MessageActionsViewModel @AssistedInject constructor(@Assisted
// TODO sent by me or sufficient power level // TODO sent by me or sufficient power level
} }
private fun ArrayList<EventSharedAction>.addActionsForSyncedState(timelineEvent: TimelineEvent, private suspend fun ArrayList<EventSharedAction>.addActionsForSyncedState(timelineEvent: TimelineEvent,
actionPermissions: ActionPermissions, actionPermissions: ActionPermissions,
messageContent: MessageContent?, messageContent: MessageContent?,
msgType: String?) { msgType: String?) {

View File

@ -139,20 +139,18 @@ class MessageInformationDataFactory @Inject constructor(private val session: Ses
} }
} }
private fun getE2EDecoration(roomSummary: RoomSummary?, event: TimelineEvent): E2EDecoration { private fun getE2EDecoration(roomSummary: RoomSummary?, event: TimelineEvent): E2EDecoration = runBlocking{
if (event.root.sendState != SendState.SYNCED) if (event.root.sendState != SendState.SYNCED)
return E2EDecoration.NONE return@runBlocking E2EDecoration.NONE
if (!roomSummary?.isEncrypted.orFalse()) if (!roomSummary?.isEncrypted.orFalse())
return E2EDecoration.NONE return@runBlocking E2EDecoration.NONE
val isUserVerified = runBlocking { val isUserVerified = session.cryptoService().crossSigningService().getUserCrossSigningKeys(event.root.senderId ?: "")?.isTrusted().orFalse()
session.cryptoService().crossSigningService().getUserCrossSigningKeys(event.root.senderId ?: "")?.isTrusted().orFalse()
}
if (!isUserVerified) { if (!isUserVerified) {
return E2EDecoration.NONE return@runBlocking E2EDecoration.NONE
} }
val ts = roomSummary?.encryptionEventTs ?: 0 val ts = roomSummary?.encryptionEventTs ?: 0
val eventTs = event.root.originServerTs ?: 0 val eventTs = event.root.originServerTs ?: 0
return if (event.isEncrypted()) { return@runBlocking if (event.isEncrypted()) {
// Do not decorate failed to decrypt, or redaction (we lost sender device info) // Do not decorate failed to decrypt, or redaction (we lost sender device info)
if (event.root.getClearType() == EventType.ENCRYPTED || event.root.isRedacted()) { if (event.root.getClearType() == EventType.ENCRYPTED || event.root.isRedacted()) {
E2EDecoration.NONE E2EDecoration.NONE

View File

@ -190,7 +190,7 @@ class NotifiableEventResolver @Inject constructor(
} }
} }
private fun TimelineEvent.attemptToDecryptIfNeeded(session: Session) { private suspend fun TimelineEvent.attemptToDecryptIfNeeded(session: Session) {
if (root.isEncrypted() && root.mxDecryptionResult == null) { if (root.isEncrypted() && root.mxDecryptionResult == null) {
// TODO use a global event decryptor? attache to session and that listen to new sessionId? // TODO use a global event decryptor? attache to session and that listen to new sessionId?
// for now decrypt sync // for now decrypt sync

View File

@ -70,12 +70,10 @@ import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import me.gujun.android.span.span import me.gujun.android.span.span
import org.matrix.android.sdk.api.MatrixCallback
import org.matrix.android.sdk.api.extensions.getFingerprintHumanReadable import org.matrix.android.sdk.api.extensions.getFingerprintHumanReadable
import org.matrix.android.sdk.api.raw.RawService import org.matrix.android.sdk.api.raw.RawService
import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified import org.matrix.android.sdk.internal.crypto.crosssigning.isVerified
import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo import org.matrix.android.sdk.internal.crypto.model.rest.DeviceInfo
import org.matrix.android.sdk.internal.crypto.model.rest.DevicesListResponse
import javax.inject.Inject import javax.inject.Inject
class VectorSettingsSecurityPrivacyFragment @Inject constructor( class VectorSettingsSecurityPrivacyFragment @Inject constructor(
@ -524,7 +522,7 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
/** /**
* Build the cryptography preference section. * Build the cryptography preference section.
*/ */
private fun refreshCryptographyPreference(devices: List<DeviceInfo>) { private suspend fun refreshCryptographyPreference(devices: List<DeviceInfo>) {
showDeviceListPref.isEnabled = devices.isNotEmpty() showDeviceListPref.isEnabled = devices.isNotEmpty()
showDeviceListPref.summary = resources.getQuantityString(R.plurals.settings_active_sessions_count, devices.size, devices.size) showDeviceListPref.summary = resources.getQuantityString(R.plurals.settings_active_sessions_count, devices.size, devices.size)
@ -580,28 +578,19 @@ class VectorSettingsSecurityPrivacyFragment @Inject constructor(
// ============================================================================================================== // ==============================================================================================================
private fun refreshMyDevice() { private fun refreshMyDevice() {
session.cryptoService().getUserDevices(session.myUserId).map { viewLifecycleOwner.lifecycleScope.launchWhenResumed {
DeviceInfo( session.cryptoService().getUserDevices(session.myUserId).map {
userId = session.myUserId, DeviceInfo(
deviceId = it.deviceId, userId = session.myUserId,
displayName = it.displayName() deviceId = it.deviceId,
) displayName = it.displayName()
}.let { )
refreshCryptographyPreference(it) }.let {
refreshCryptographyPreference(it)
}
// TODO Move to a ViewModel...
val devicesList = session.cryptoService().fetchDevicesList()
refreshCryptographyPreference(devicesList)
} }
// TODO Move to a ViewModel...
session.cryptoService().fetchDevicesList(object : MatrixCallback<DevicesListResponse> {
override fun onSuccess(data: DevicesListResponse) {
if (isAdded) {
refreshCryptographyPreference(data.devices.orEmpty())
}
}
override fun onFailure(failure: Throwable) {
if (isAdded) {
refreshCryptographyPreference(emptyList())
}
}
})
} }
} }

View File

@ -147,7 +147,7 @@ class DevicesViewModel @AssistedInject constructor(
.sample(5_000) .sample(5_000)
.onEach { .onEach {
// If we have a new crypto device change, we might want to trigger refresh of device info // If we have a new crypto device change, we might want to trigger refresh of device info
session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) session.cryptoService().fetchDevicesList()
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)
@ -160,7 +160,7 @@ class DevicesViewModel @AssistedInject constructor(
refreshSource.stream().throttleFirst(4_000) refreshSource.stream().throttleFirst(4_000)
.onEach { .onEach {
session.cryptoService().fetchDevicesList(NoOpMatrixCallback()) session.cryptoService().fetchDevicesList()
session.cryptoService().downloadKeys(listOf(session.myUserId), true) session.cryptoService().downloadKeys(listOf(session.myUserId), true)
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)