Suspend api: continue moving away from callback
This commit is contained in:
parent
9c6fccab1d
commit
ed84e38a9b
@ -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>>
|
||||||
|
|
||||||
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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 =
|
||||||
|
@ -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), "")
|
||||||
|
@ -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 })
|
||||||
|
@ -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
|
||||||
|
@ -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,
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -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?) {
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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)
|
||||||
|
Loading…
Reference in New Issue
Block a user