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.GossipingEventEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.IncomingGossipingRequestEntityFields 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.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.MyDeviceLastSeenInfoEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields import im.vector.matrix.android.internal.crypto.store.db.model.OutgoingGossipingRequestEntityFields
import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntityFields import im.vector.matrix.android.internal.crypto.store.db.model.TrustLevelEntityFields

View file

@ -423,7 +423,7 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(
} }
} catch (failure: Throwable) { } catch (failure: Throwable) {
// Just ignore for now // 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 @EpoxyAttribute
var trusted: DeviceTrustLevel? = null var trusted: DeviceTrustLevel? = null
@EpoxyAttribute
var e2Ecapable: Boolean = true
@EpoxyAttribute @EpoxyAttribute
var legacyMode: Boolean = false var legacyMode: Boolean = false
@ -79,7 +82,9 @@ abstract class DeviceItem : VectorEpoxyModel<DeviceItem.Holder>() {
trusted trusted
) )
holder.trustIcon.setImageResource(shield) if (e2Ecapable) {
holder.trustIcon.setImageResource(shield)
}
val detailedModeLabels = listOf( val detailedModeLabels = listOf(
holder.displayNameLabelText, holder.displayNameLabelText,

View file

@ -21,9 +21,7 @@ import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized 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.crosssigning.DeviceTrustLevel
import im.vector.matrix.android.internal.crypto.model.CryptoDeviceInfo
import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo import im.vector.matrix.android.internal.crypto.model.rest.DeviceInfo
import im.vector.riotx.R import im.vector.riotx.R
import im.vector.riotx.core.epoxy.errorWithRetryItem import im.vector.riotx.core.epoxy.errorWithRetryItem
@ -73,20 +71,19 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
listener { callback?.retry() } listener { callback?.retry() }
} }
is Success -> 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>, private fun buildDevicesList(devices: List<DeviceFullInfo>,
cryptoDevices: List<CryptoDeviceInfo>?,
myDeviceId: String, myDeviceId: String,
legacyMode: Boolean, legacyMode: Boolean,
currentSessionCrossTrusted: Boolean) { currentSessionCrossTrusted: Boolean) {
devices devices
.firstOrNull { .firstOrNull {
it.deviceId == myDeviceId it.deviceInfo.deviceId == myDeviceId
}?.let { deviceInfo -> }?.let { fullInfo ->
val deviceInfo = fullInfo.deviceInfo
// Current device // Current device
genericItemHeader { genericItemHeader {
id("current") id("current")
@ -102,6 +99,7 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
detailedMode(vectorPreferences.developerMode()) detailedMode(vectorPreferences.developerMode())
deviceInfo(deviceInfo) deviceInfo(deviceInfo)
currentDevice(true) currentDevice(true)
e2Ecapable(true)
itemClickAction { callback?.onDeviceClicked(deviceInfo) } itemClickAction { callback?.onDeviceClicked(deviceInfo) }
trusted(DeviceTrustLevel(currentSessionCrossTrusted, true)) trusted(DeviceTrustLevel(currentSessionCrossTrusted, true))
} }
@ -129,12 +127,11 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
devices devices
.filter { .filter {
it.deviceId != myDeviceId it.deviceInfo.deviceId != myDeviceId
} }
// sort before display: most recent first .forEachIndexed { idx, deviceInfoPair ->
.sortByLastSeen() val deviceInfo = deviceInfoPair.deviceInfo
.forEachIndexed { idx, deviceInfo -> val cryptoInfo = deviceInfoPair.cryptoDeviceInfo
val isCurrentDevice = deviceInfo.deviceId == myDeviceId
deviceItem { deviceItem {
id("device$idx") id("device$idx")
legacyMode(legacyMode) legacyMode(legacyMode)
@ -143,9 +140,10 @@ class DevicesController @Inject constructor(private val errorFormatter: ErrorFor
colorProvider(colorProvider) colorProvider(colorProvider)
detailedMode(vectorPreferences.developerMode()) detailedMode(vectorPreferences.developerMode())
deviceInfo(deviceInfo) deviceInfo(deviceInfo)
currentDevice(isCurrentDevice) currentDevice(false)
itemClickAction { callback?.onDeviceClicked(deviceInfo) } 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.android.internal.util.awaitCallback
import im.vector.matrix.rx.rx import im.vector.matrix.rx.rx
import im.vector.riotx.core.platform.VectorViewModel import im.vector.riotx.core.platform.VectorViewModel
import io.reactivex.Observable
import io.reactivex.functions.BiFunction
import io.reactivex.subjects.PublishSubject import io.reactivex.subjects.PublishSubject
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
data class DevicesViewState( data class DevicesViewState(
val myDeviceId: String = "", val myDeviceId: String = "",
val devices: Async<List<DeviceInfo>> = Uninitialized, // val devices: Async<List<DeviceInfo>> = Uninitialized,
val cryptoDevices: Async<List<CryptoDeviceInfo>> = Uninitialized, // val cryptoDevices: Async<List<CryptoDeviceInfo>> = Uninitialized,
val devices: Async<List<DeviceFullInfo>> = Uninitialized,
// TODO Replace by isLoading boolean // TODO Replace by isLoading boolean
val request: Async<Unit> = Uninitialized, val request: Async<Unit> = Uninitialized,
val hasAccountCrossSigning: Boolean = false, val hasAccountCrossSigning: Boolean = false,
val accountCrossSigningIsTrusted: Boolean = false val accountCrossSigningIsTrusted: Boolean = false
) : MvRxState ) : MvRxState
data class DeviceFullInfo(
val deviceInfo: DeviceInfo,
val cryptoDeviceInfo: CryptoDeviceInfo?
)
class DevicesViewModel @AssistedInject constructor( class DevicesViewModel @AssistedInject constructor(
@Assisted initialState: DevicesViewState, @Assisted initialState: DevicesViewState,
private val session: Session private val session: Session
@ -92,6 +99,26 @@ class DevicesViewModel @AssistedInject constructor(
myDeviceId = session.sessionParams.credentials.deviceId ?: "" 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) session.rx().liveCrossSigningInfo(session.myUserId)
.execute { .execute {
copy( copy(
@ -101,18 +128,27 @@ class DevicesViewModel @AssistedInject constructor(
} }
session.cryptoService().verificationService().addListener(this) session.cryptoService().verificationService().addListener(this)
session.rx().liveMyDeviceInfo() // session.rx().liveMyDeviceInfo()
.execute { // .execute {
copy( // copy(
devices = it // devices = it
) // )
} // }
session.rx().liveUserCryptoDevices(session.myUserId) session.rx().liveUserCryptoDevices(session.myUserId)
.execute { .distinct()
copy( .throttleLast(5_000, TimeUnit.MILLISECONDS)
cryptoDevices = it .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) refreshPublisher.throttleFirst(4_000, TimeUnit.MILLISECONDS)
.subscribe { .subscribe {
@ -142,66 +178,17 @@ class DevicesViewModel @AssistedInject constructor(
*/ */
private fun queryRefreshDevicesList() { private fun queryRefreshDevicesList() {
refreshPublisher.onNext(Unit) 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) { override fun handle(action: DevicesAction) {
return when (action) { return when (action) {
is DevicesAction.Refresh -> queryRefreshDevicesList() is DevicesAction.Refresh -> queryRefreshDevicesList()
is DevicesAction.Delete -> handleDelete(action) is DevicesAction.Delete -> handleDelete(action)
is DevicesAction.Password -> handlePassword(action) is DevicesAction.Password -> handlePassword(action)
is DevicesAction.Rename -> handleRename(action) is DevicesAction.Rename -> handleRename(action)
is DevicesAction.PromptRename -> handlePromptRename(action) is DevicesAction.PromptRename -> handlePromptRename(action)
is DevicesAction.VerifyMyDevice -> handleInteractiveVerification(action) is DevicesAction.VerifyMyDevice -> handleInteractiveVerification(action)
is DevicesAction.CompleteSecurity -> handleCompleteSecurity() is DevicesAction.CompleteSecurity -> handleCompleteSecurity()
is DevicesAction.MarkAsManuallyVerified -> handleVerifyManually(action) is DevicesAction.MarkAsManuallyVerified -> handleVerifyManually(action)
is DevicesAction.VerifyMyDeviceManually -> handleShowDeviceCryptoInfo(action) is DevicesAction.VerifyMyDeviceManually -> handleShowDeviceCryptoInfo(action)
} }
@ -218,10 +205,10 @@ class DevicesViewModel @AssistedInject constructor(
} }
private fun handleShowDeviceCryptoInfo(action: DevicesAction.VerifyMyDeviceManually) = withState { state -> private fun handleShowDeviceCryptoInfo(action: DevicesAction.VerifyMyDeviceManually) = withState { state ->
state.cryptoDevices.invoke() state.devices.invoke()
?.firstOrNull { it.deviceId == action.deviceId } ?.firstOrNull { it.cryptoDeviceInfo?.deviceId == action.deviceId }
?.let { ?.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 -> 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) { if (info != null) {
_viewEvents.post(DevicesViewEvents.PromptRenameDevice(info)) _viewEvents.post(DevicesViewEvents.PromptRenameDevice(info.deviceInfo))
} }
} }