Fix / Regression on non e2e device

+ migrate to new rx objects
This commit is contained in:
Valere 2020-04-29 12:35:22 +02:00
parent a806f70b35
commit 0f00597444
5 changed files with 81 additions and 92 deletions

View File

@ -28,7 +28,6 @@ import im.vector.matrix.android.internal.crypto.store.db.model.DeviceInfoEntityF
import im.vector.matrix.android.internal.crypto.store.db.model.GossipingEventEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.KeyInfoEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntity
import im.vector.matrix.android.internal.crypto.store.db.model.MyDeviceLastSeenInfoEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntityFields

View File

@ -423,7 +423,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
}
} catch (failure: Throwable) {
// Just ignore for now
Timber.e(failure,"## Failed to restore backup after SSSS recovery")
Timber.e(failure, "## Failed to restore backup after SSSS recovery")
}
}
}

View File

@ -57,6 +57,9 @@ abstract class DeviceItem : VectorEpoxyModel<DeviceItem.Holder>() {
@EpoxyAttribute
var trusted: DeviceTrustLevel? = null
@EpoxyAttribute
var e2Ecapable: Boolean = true
@EpoxyAttribute
var legacyMode: Boolean = false
@ -79,7 +82,9 @@ abstract class DeviceItem : VectorEpoxyModel<DeviceItem.Holder>() {
trusted
)
holder.trustIcon.setImageResource(shield)
if (e2Ecapable) {
holder.trustIcon.setImageResource(shield)
}
val detailedModeLabels = listOf(
holder.displayNameLabelText,

View File

@ -21,9 +21,7 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import im.vector.matrix.android.api.extensions.sortByLastSeen
import im.vector.matrix.android.internal.crypto.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.riotx.R
import im.vector.riotx.core.epoxy.errorWithRetryItem
@ -73,20 +71,19 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
listener { callback?.retry() }
}
is Success ->
buildDevicesList(devices(), state.cryptoDevices(), state.myDeviceId, !state.hasAccountCrossSigning, state.accountCrossSigningIsTrusted)
buildDevicesList(devices(), state.myDeviceId, !state.hasAccountCrossSigning, state.accountCrossSigningIsTrusted)
}
}
private fun buildDevicesList(devices: List<DeviceInfo>,
cryptoDevices: List<CryptoDeviceInfo>?,
private fun buildDevicesList(devices: List<DeviceFullInfo>,
myDeviceId: String,
legacyMode: Boolean,
currentSessionCrossTrusted: Boolean) {
devices
.firstOrNull {
it.deviceId == myDeviceId
}?.let { deviceInfo ->
it.deviceInfo.deviceId == myDeviceId
}?.let { fullInfo ->
val deviceInfo = fullInfo.deviceInfo
// Current device
genericItemHeader {
id("current")
@ -102,6 +99,7 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
detailedMode(vectorPreferences.developerMode())
deviceInfo(deviceInfo)
currentDevice(true)
e2Ecapable(true)
itemClickAction { callback?.onDeviceClicked(deviceInfo) }
trusted(DeviceTrustLevel(currentSessionCrossTrusted, true))
}
@ -129,12 +127,11 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
devices
.filter {
it.deviceId != myDeviceId
it.deviceInfo.deviceId != myDeviceId
}
// sort before display: most recent first
.sortByLastSeen()
.forEachIndexed { idx, deviceInfo ->
val isCurrentDevice = deviceInfo.deviceId == myDeviceId
.forEachIndexed { idx, deviceInfoPair ->
val deviceInfo = deviceInfoPair.deviceInfo
val cryptoInfo = deviceInfoPair.cryptoDeviceInfo
deviceItem {
id("device$idx")
legacyMode(legacyMode)
@ -143,9 +140,10 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
colorProvider(colorProvider)
detailedMode(vectorPreferences.developerMode())
deviceInfo(deviceInfo)
currentDevice(isCurrentDevice)
currentDevice(false)
itemClickAction { callback?.onDeviceClicked(deviceInfo) }
trusted(cryptoDevices?.firstOrNull { it.deviceId == deviceInfo.deviceId }?.trustLevel)
e2Ecapable(cryptoInfo != null)
trusted(cryptoInfo?.trustLevel)
}
}
}

View File

@ -44,20 +44,27 @@ import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.matrix.android.internal.util.awaitCallback
import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
data class DevicesViewState(
val myDeviceId: String = "",
val devices: Async<List<DeviceInfo>> = Uninitialized,
val cryptoDevices: Async<List<CryptoDeviceInfo>> = Uninitialized,
// val devices: Async<List<DeviceInfo>> = Uninitialized,
// val cryptoDevices: Async<List<CryptoDeviceInfo>> = Uninitialized,
val devices: Async<List<DeviceFullInfo>> = Uninitialized,
// TODO Replace by isLoading boolean
val request: Async<Unit> = Uninitialized,
val hasAccountCrossSigning: Boolean = false,
val accountCrossSigningIsTrusted: Boolean = false
) : MvRxState
data class DeviceFullInfo(
val deviceInfo: DeviceInfo,
val cryptoDeviceInfo: CryptoDeviceInfo?
)
class DevicesViewModel @AssistedInject constructor(
@Assisted initialState: DevicesViewState,
private val session: Session
@ -92,6 +99,26 @@ class DevicesViewModel @AssistedInject constructor(
myDeviceId = session.sessionParams.credentials.deviceId ?: ""
)
}
Observable.combineLatest<List<CryptoDeviceInfo>, List<DeviceInfo>, List<DeviceFullInfo>>(
session.rx().liveUserCryptoDevices(session.myUserId),
session.rx().liveMyDeviceInfo(),
BiFunction { cryptoList, infoList ->
infoList
.sortedByDescending { it.lastSeenTs }
.map { deviceInfo ->
val cryptoDeviceInfo = cryptoList.firstOrNull { it.deviceId == deviceInfo.deviceId }
DeviceFullInfo(deviceInfo, cryptoDeviceInfo)
}
}
)
.distinct()
.execute { async ->
copy(
devices = async
)
}
session.rx().liveCrossSigningInfo(session.myUserId)
.execute {
copy(
@ -101,18 +128,27 @@ class DevicesViewModel @AssistedInject constructor(
}
session.cryptoService().verificationService().addListener(this)
session.rx().liveMyDeviceInfo()
.execute {
copy(
devices = it
)
}
// session.rx().liveMyDeviceInfo()
// .execute {
// copy(
// devices = it
// )
// }
session.rx().liveUserCryptoDevices(session.myUserId)
.execute {
copy(
cryptoDevices = it
)
}
.distinct()
.throttleLast(5_000, TimeUnit.MILLISECONDS)
.subscribe {
// If we have a new crypto device change, we might want to trigger refresh of device info
session.cryptoService().fetchDevicesList(NoOpMatrixCallback())
}.disposeOnClear()
// session.rx().liveUserCryptoDevices(session.myUserId)
// .execute {
// copy(
// cryptoDevices = it
// )
// }
refreshPublisher.throttleFirst(4_000, TimeUnit.MILLISECONDS)
.subscribe {
@ -142,66 +178,17 @@ class DevicesViewModel @AssistedInject constructor(
*/
private fun queryRefreshDevicesList() {
refreshPublisher.onNext(Unit)
// if (!session.sessionParams.credentials.deviceId.isNullOrEmpty()) {
// // display something asap
// val localKnown = session.cryptoService().getUserDevices(session.myUserId).map {
// DeviceInfo(
// user_id = session.myUserId,
// deviceId = it.deviceId,
// displayName = it.displayName()
// )
// }
//
// setState {
// copy(
// // Keep known list if we have it, and let refresh go in backgroung
// devices = this.devices.takeIf { it is Success } ?: Success(localKnown)
// )
// }
//
// session.cryptoService().fetchDevicesList(object : MatrixCallback<DevicesListResponse> {
// override fun onSuccess(data: DevicesListResponse) {
// setState {
// copy(
// myDeviceId = session.sessionParams.credentials.deviceId ?: "",
// devices = Success(data.devices.orEmpty())
// )
// }
// }
//
// override fun onFailure(failure: Throwable) {
// setState {
// copy(
// devices = Fail(failure)
// )
// }
// }
// })
//
// // Put cached state
// setState {
// copy(
// myDeviceId = session.sessionParams.credentials.deviceId ?: "",
// cryptoDevices = Success(session.cryptoService().getUserDevices(session.myUserId))
// )
// }
//
//
// } else {
// // Should not happen
// }
}
override fun handle(action: DevicesAction) {
return when (action) {
is DevicesAction.Refresh -> queryRefreshDevicesList()
is DevicesAction.Delete -> handleDelete(action)
is DevicesAction.Password -> handlePassword(action)
is DevicesAction.Rename -> handleRename(action)
is DevicesAction.PromptRename -> handlePromptRename(action)
is DevicesAction.VerifyMyDevice -> handleInteractiveVerification(action)
is DevicesAction.CompleteSecurity -> handleCompleteSecurity()
is DevicesAction.Refresh -> queryRefreshDevicesList()
is DevicesAction.Delete -> handleDelete(action)
is DevicesAction.Password -> handlePassword(action)
is DevicesAction.Rename -> handleRename(action)
is DevicesAction.PromptRename -> handlePromptRename(action)
is DevicesAction.VerifyMyDevice -> handleInteractiveVerification(action)
is DevicesAction.CompleteSecurity -> handleCompleteSecurity()
is DevicesAction.MarkAsManuallyVerified -> handleVerifyManually(action)
is DevicesAction.VerifyMyDeviceManually -> handleShowDeviceCryptoInfo(action)
}
@ -218,10 +205,10 @@ class DevicesViewModel @AssistedInject constructor(
}
private fun handleShowDeviceCryptoInfo(action: DevicesAction.VerifyMyDeviceManually) = withState { state ->
state.cryptoDevices.invoke()
?.firstOrNull { it.deviceId == action.deviceId }
state.devices.invoke()
?.firstOrNull { it.cryptoDeviceInfo?.deviceId == action.deviceId }
?.let {
_viewEvents.post(DevicesViewEvents.ShowManuallyVerify(it))
_viewEvents.post(DevicesViewEvents.ShowManuallyVerify(it.cryptoDeviceInfo!!))
}
}
@ -246,9 +233,9 @@ class DevicesViewModel @AssistedInject constructor(
}
private fun handlePromptRename(action: DevicesAction.PromptRename) = withState { state ->
val info = state.devices.invoke()?.firstOrNull { it.deviceId == action.deviceId }
val info = state.devices.invoke()?.firstOrNull { it.deviceInfo.deviceId == action.deviceId }
if (info != null) {
_viewEvents.post(DevicesViewEvents.PromptRenameDevice(info))
_viewEvents.post(DevicesViewEvents.PromptRenameDevice(info.deviceInfo))
}
}