Implement user verification (#4294)
* Add support for starting verification of a user * Add support for replying to incoming user verification requests * Add reset recovery key button and previews to `ChooseSelfVerificationModeView` * Add 'Profile' item in room details screen * Update screenshots * Remove `showDeviceVerifiedScreen` parameter from `NavTarget.UseAnotherDevice` * Allow exiting the FTUE flow, which will close the app. The previous state will be restored when the app is reopened. * When outgoing verification fails, move to the `Canceled` state. Then, when resetting the state machine state also reset the verification service. --------- Co-authored-by: ElementBot <android@element.io>
This commit is contained in:
parent
2ce1b17dae
commit
f73c0e42a4
145 changed files with 1662 additions and 830 deletions
|
|
@ -8,13 +8,14 @@
|
|||
package io.element.android.libraries.matrix.impl.verification
|
||||
|
||||
import io.element.android.libraries.core.data.tryOrNull
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationData
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationService
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationServiceListener
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerifiedStatus
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationEmoji
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationFlowState
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationRequest
|
||||
import io.element.android.libraries.matrix.impl.util.cancelAndDestroy
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
|
|
@ -50,6 +51,8 @@ class RustSessionVerificationService(
|
|||
isSyncServiceReady: Flow<Boolean>,
|
||||
private val sessionCoroutineScope: CoroutineScope,
|
||||
) : SessionVerificationService, SessionVerificationControllerDelegate {
|
||||
private var currentVerificationRequest: VerificationRequest? = null
|
||||
|
||||
private val encryptionService: Encryption = client.encryption()
|
||||
private lateinit var verificationController: SessionVerificationController
|
||||
|
||||
|
|
@ -88,10 +91,8 @@ class RustSessionVerificationService(
|
|||
verificationStatus == SessionVerifiedStatus.NotVerified
|
||||
}
|
||||
|
||||
private var isOwnVerification = true
|
||||
|
||||
override fun didReceiveVerificationRequest(details: RustSessionVerificationRequestDetails) {
|
||||
listener?.onIncomingSessionRequest(details.map())
|
||||
listener?.onIncomingSessionRequest(details.toVerificationRequest(UserId(client.userId())))
|
||||
}
|
||||
|
||||
private var listener: SessionVerificationServiceListener? = null
|
||||
|
|
@ -111,9 +112,16 @@ class RustSessionVerificationService(
|
|||
this.listener = listener
|
||||
}
|
||||
|
||||
override suspend fun requestVerification() = tryOrFail {
|
||||
override suspend fun requestCurrentSessionVerification() = tryOrFail {
|
||||
initVerificationControllerIfNeeded()
|
||||
verificationController.requestDeviceVerification()
|
||||
currentVerificationRequest = VerificationRequest.Outgoing.CurrentSession
|
||||
}
|
||||
|
||||
override suspend fun requestUserVerification(userId: UserId) = tryOrFail {
|
||||
initVerificationControllerIfNeeded()
|
||||
verificationController.requestUserVerification(userId.value)
|
||||
currentVerificationRequest = VerificationRequest.Outgoing.User(userId)
|
||||
}
|
||||
|
||||
override suspend fun cancelVerification() = tryOrFail {
|
||||
|
|
@ -130,16 +138,16 @@ class RustSessionVerificationService(
|
|||
verificationController.startSasVerification()
|
||||
}
|
||||
|
||||
override suspend fun acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) = tryOrFail {
|
||||
isOwnVerification = false
|
||||
override suspend fun acknowledgeVerificationRequest(verificationRequest: VerificationRequest.Incoming) = tryOrFail {
|
||||
initVerificationControllerIfNeeded()
|
||||
verificationController.acknowledgeVerificationRequest(
|
||||
senderId = details.senderId.value,
|
||||
flowId = details.flowId.value,
|
||||
senderId = verificationRequest.details.senderProfile.userId.value,
|
||||
flowId = verificationRequest.details.flowId.value,
|
||||
)
|
||||
}
|
||||
|
||||
override suspend fun acceptVerificationRequest() = tryOrFail {
|
||||
Timber.d("Accepting incoming verification request")
|
||||
verificationController.acceptVerificationRequest()
|
||||
}
|
||||
|
||||
|
|
@ -183,7 +191,7 @@ class RustSessionVerificationService(
|
|||
}
|
||||
}
|
||||
.onSuccess {
|
||||
if (isOwnVerification) {
|
||||
if (currentVerificationRequest is VerificationRequest.Outgoing.CurrentSession) {
|
||||
// Try waiting for the final recovery state for better UX, but don't block the verification state on it
|
||||
tryOrNull {
|
||||
withTimeout(10.seconds) {
|
||||
|
|
@ -215,7 +223,7 @@ class RustSessionVerificationService(
|
|||
// end-region
|
||||
|
||||
override suspend fun reset(cancelAnyPendingVerificationAttempt: Boolean) {
|
||||
isOwnVerification = true
|
||||
currentVerificationRequest = null
|
||||
if (isReady.value && cancelAnyPendingVerificationAttempt) {
|
||||
// Cancel any pending verification attempt
|
||||
tryOrNull { verificationController.cancelVerification() }
|
||||
|
|
|
|||
|
|
@ -11,12 +11,28 @@ import io.element.android.libraries.matrix.api.core.DeviceId
|
|||
import io.element.android.libraries.matrix.api.core.FlowId
|
||||
import io.element.android.libraries.matrix.api.core.UserId
|
||||
import io.element.android.libraries.matrix.api.verification.SessionVerificationRequestDetails
|
||||
import io.element.android.libraries.matrix.api.verification.VerificationRequest
|
||||
import org.matrix.rustcomponents.sdk.SessionVerificationRequestDetails as RustSessionVerificationRequestDetails
|
||||
import org.matrix.rustcomponents.sdk.UserProfile as RustUserProfile
|
||||
|
||||
fun RustSessionVerificationRequestDetails.map() = SessionVerificationRequestDetails(
|
||||
senderId = UserId(senderProfile.userId),
|
||||
senderProfile = senderProfile.map(),
|
||||
flowId = FlowId(flowId),
|
||||
deviceId = DeviceId(deviceId),
|
||||
displayName = senderProfile.displayName,
|
||||
firstSeenTimestamp = firstSeenTimestamp.toLong(),
|
||||
)
|
||||
|
||||
fun RustUserProfile.map() = SessionVerificationRequestDetails.SenderProfile(
|
||||
userId = UserId(userId),
|
||||
displayName = displayName,
|
||||
avatarUrl = avatarUrl,
|
||||
)
|
||||
|
||||
fun RustSessionVerificationRequestDetails.toVerificationRequest(currentUserId: UserId): VerificationRequest.Incoming {
|
||||
val details = map()
|
||||
return if (currentUserId == details.senderProfile.userId) {
|
||||
VerificationRequest.Incoming.OtherSession(details)
|
||||
} else {
|
||||
VerificationRequest.Incoming.User(details)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue