mirror of
https://github.com/element-hq/element-android
synced 2024-11-28 04:52:00 +03:00
Fix / Regression on non e2e device
+ migrate to new rx objects
This commit is contained in:
parent
a806f70b35
commit
0f00597444
5 changed files with 81 additions and 92 deletions
|
@ -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
|
||||||
|
|
|
@ -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")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue