happy path qr kotlin verif

This commit is contained in:
Valere 2022-11-17 22:42:47 +01:00
parent cf366f7a9c
commit 5c82bdba38
53 changed files with 4009 additions and 3607 deletions

View File

@ -40,7 +40,6 @@ import org.matrix.android.sdk.api.session.crypto.model.OlmDecryptionResult
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.events.model.EventType import org.matrix.android.sdk.api.session.events.model.EventType
import org.matrix.android.sdk.api.session.events.model.toModel import org.matrix.android.sdk.api.session.events.model.toModel
import org.matrix.android.sdk.api.session.getRoom import org.matrix.android.sdk.api.session.getRoom

View File

@ -561,7 +561,6 @@ class SASTest : InstrumentedTest {
val requestID = req.transactionId val requestID = req.transactionId
Log.v("TEST", "== requestID is $requestID") Log.v("TEST", "== requestID is $requestID")
testHelper.retryPeriodically { testHelper.retryPeriodically {

View File

@ -1233,7 +1233,7 @@ internal class DefaultVerificationService @Inject constructor(
} }
override suspend fun reciprocateQRVerification(otherUserId: String, requestId: String, scannedData: String): String? { override suspend fun reciprocateQRVerification(otherUserId: String, requestId: String, scannedData: String): String? {
val deferred = CompletableDeferred<VerificationTransaction>() val deferred = CompletableDeferred<VerificationTransaction?>()
stateMachine.send( stateMachine.send(
VerificationIntent.ActionReciprocateQrVerification( VerificationIntent.ActionReciprocateQrVerification(
otherUserId = otherUserId, otherUserId = otherUserId,
@ -1242,7 +1242,7 @@ internal class DefaultVerificationService @Inject constructor(
deferred = deferred deferred = deferred
) )
) )
return deferred.await().transactionId return deferred.await()?.transactionId
} }
override suspend fun requestKeyVerificationInDMs( override suspend fun requestKeyVerificationInDMs(

View File

@ -16,41 +16,67 @@
package org.matrix.android.sdk.internal.crypto.verification package org.matrix.android.sdk.internal.crypto.verification
import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.channels.Channel
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.QRCodeVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeData
import org.matrix.android.sdk.internal.crypto.verification.qrcode.toEncodedString
class KotlinQRVerification( internal class KotlinQRVerification(
override val qrCodeText: String?, private val channel: Channel<VerificationIntent>,
override val state: VerificationTxState, var qrCodeData: QrCodeData?,
override val method: VerificationMethod, override val method: VerificationMethod,
override val transactionId: String, override val transactionId: String,
override val otherUserId: String, override val otherUserId: String,
override val otherDeviceId: String?, override val otherDeviceId: String?,
override val isIncoming: Boolean) : QrCodeVerificationTransaction { override val isIncoming: Boolean,
var state: QRCodeVerificationState,
val isToDevice: Boolean
) : QrCodeVerificationTransaction {
override suspend fun userHasScannedOtherQrCode(otherQrCodeText: String) { override fun state() = state
TODO("Not yet implemented")
} override val qrCodeText: String?
get() = qrCodeData?.toEncodedString()
//
// var userMSKKeyToTrust: String? = null
// var deviceKeysToTrust = mutableListOf<String>()
// override suspend fun userHasScannedOtherQrCode(otherQrCodeText: String) {
// TODO("Not yet implemented")
// }
override suspend fun otherUserScannedMyQrCode() { override suspend fun otherUserScannedMyQrCode() {
TODO("Not yet implemented") val deferred = CompletableDeferred<Unit>()
channel.send(
VerificationIntent.ActionConfirmCodeWasScanned(otherUserId, transactionId, deferred)
)
deferred.await()
} }
override suspend fun otherUserDidNotScannedMyQrCode() { override suspend fun otherUserDidNotScannedMyQrCode() {
TODO("Not yet implemented") val deferred = CompletableDeferred<Unit>()
channel.send(
// TODO what cancel code??
VerificationIntent.ActionCancel(transactionId, deferred)
)
deferred.await()
} }
override suspend fun cancel() { override suspend fun cancel() {
TODO("Not yet implemented") cancel(CancelCode.User)
} }
override suspend fun cancel(code: CancelCode) { override suspend fun cancel(code: CancelCode) {
TODO("Not yet implemented") val deferred = CompletableDeferred<Unit>()
channel.send(
VerificationIntent.ActionCancel(transactionId, deferred)
)
deferred.await()
} }
override fun isToDeviceTransport(): Boolean { override fun isToDeviceTransport() = isToDevice
TODO("Not yet implemented")
}
} }

View File

@ -22,6 +22,7 @@ import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.SasMode import org.matrix.android.sdk.api.session.crypto.verification.SasMode
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.KEY_AGREEMENT_V1 import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.KEY_AGREEMENT_V1
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.KEY_AGREEMENT_V2 import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.KEY_AGREEMENT_V2
@ -32,7 +33,6 @@ import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTra
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.SAS_MAC_SHA256 import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.SAS_MAC_SHA256
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.SAS_MAC_SHA256_LONGKDF import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction.Companion.SAS_MAC_SHA256_LONGKDF
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.events.model.RelationType import org.matrix.android.sdk.api.session.events.model.RelationType
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationAcceptContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationAcceptContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationKeyContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationKeyContent
@ -50,9 +50,8 @@ import org.matrix.olm.OlmSAS
import timber.log.Timber import timber.log.Timber
import java.util.Locale import java.util.Locale
internal class SasV1Transaction( internal class KotlinSasTransaction(
private val channel: Channel<VerificationIntent>, private val channel: Channel<VerificationIntent>,
override var state: VerificationTxState,
override val transactionId: String, override val transactionId: String,
override val otherUserId: String, override val otherUserId: String,
private val myUserId: String, private val myUserId: String,
@ -63,6 +62,7 @@ internal class SasV1Transaction(
override val isIncoming: Boolean, override val isIncoming: Boolean,
val startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null, val startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null,
val isToDevice: Boolean, val isToDevice: Boolean,
var state: SasTransactionState
) : SasVerificationTransaction { ) : SasVerificationTransaction {
override val method: VerificationMethod override val method: VerificationMethod
@ -207,6 +207,8 @@ internal class SasV1Transaction(
var myMac: ValidVerificationInfoMac? = null var myMac: ValidVerificationInfoMac? = null
var theirMac: ValidVerificationInfoMac? = null var theirMac: ValidVerificationInfoMac? = null
override fun state() = this.state
override fun supportsEmoji(): Boolean { override fun supportsEmoji(): Boolean {
return accepted?.shortAuthenticationStrings?.contains(SasMode.EMOJI) == true return accepted?.shortAuthenticationStrings?.contains(SasMode.EMOJI) == true
} }

View File

@ -27,19 +27,23 @@ import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.logger.LoggerTag import org.matrix.android.sdk.api.logger.LoggerTag
import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService import org.matrix.android.sdk.api.session.crypto.crosssigning.CrossSigningService
import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel import org.matrix.android.sdk.api.session.crypto.crosssigning.DeviceTrustLevel
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap import org.matrix.android.sdk.api.session.crypto.model.MXUsersDevicesMap
import org.matrix.android.sdk.api.session.crypto.model.SendToDeviceObject import org.matrix.android.sdk.api.session.crypto.model.SendToDeviceObject
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.QRCodeVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
import org.matrix.android.sdk.api.session.events.model.Content import org.matrix.android.sdk.api.session.events.model.Content
import org.matrix.android.sdk.api.session.events.model.Event import org.matrix.android.sdk.api.session.events.model.Event
@ -53,6 +57,7 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageVerification
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationStartContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationStartContent
import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent import org.matrix.android.sdk.api.session.room.model.relation.RelationDefaultContent
import org.matrix.android.sdk.internal.crypto.SecretShareManager
import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction import org.matrix.android.sdk.internal.crypto.actions.SetDeviceVerificationAction
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationCancel import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationCancel
import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationDone import org.matrix.android.sdk.internal.crypto.model.rest.KeyVerificationDone
@ -94,7 +99,8 @@ internal class VerificationActor @AssistedInject constructor(
private val localEchoEventFactory: LocalEchoEventFactory, private val localEchoEventFactory: LocalEchoEventFactory,
private val sendToDeviceTask: SendToDeviceTask, private val sendToDeviceTask: SendToDeviceTask,
private val setDeviceVerificationAction: SetDeviceVerificationAction, private val setDeviceVerificationAction: SetDeviceVerificationAction,
private val crossSigningService: dagger.Lazy<CrossSigningService> private val crossSigningService: dagger.Lazy<CrossSigningService>,
private val secretShareManager: SecretShareManager,
) { ) {
@AssistedFactory @AssistedFactory
@ -121,6 +127,34 @@ internal class VerificationActor @AssistedInject constructor(
channel.send(intent) channel.send(intent)
} }
private suspend fun withMatchingRequest(
otherUserId: String,
requestId: String,
block: suspend ((KotlinVerificationRequest) -> Unit)
) {
val matchingRequest = pendingRequests[otherUserId]
?.firstOrNull { it.requestId == requestId }
?: return Unit.also {
// Receive a transaction event with no matching request.. should ignore.
// Not supported any more to do raw start
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] request $requestId not found!")
}
if (matchingRequest.state == EVerificationState.HandledByOtherSession) {
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] ignore transaction event for $requestId handled by other")
return
}
if (matchingRequest.isFinished()) {
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] ignore transaction event for $requestId for finished request")
return
}
block.invoke(matchingRequest)
}
private suspend fun withMatchingRequest( private suspend fun withMatchingRequest(
otherUserId: String, otherUserId: String,
requestId: String, requestId: String,
@ -202,7 +236,13 @@ internal class VerificationActor @AssistedInject constructor(
handleSasStart(msg) handleSasStart(msg)
} }
is VerificationIntent.ActionReciprocateQrVerification -> { is VerificationIntent.ActionReciprocateQrVerification -> {
handleReciprocateQR(msg) handleActionReciprocateQR(msg)
}
is VerificationIntent.ActionConfirmCodeWasScanned -> {
withMatchingRequest(msg.otherUserId, msg.requestId) {
handleActionQRScanConfirmed(it)
}
msg.deferred.complete(Unit)
} }
is VerificationIntent.OnStartReceived -> { is VerificationIntent.OnStartReceived -> {
onStartReceived(msg) onStartReceived(msg)
@ -277,14 +317,15 @@ internal class VerificationActor @AssistedInject constructor(
request.state = EVerificationState.Cancelled request.state = EVerificationState.Cancelled
val cancelCode = safeValueOf(msg.validCancel.code) val cancelCode = safeValueOf(msg.validCancel.code)
request.cancelCode = cancelCode request.cancelCode = cancelCode
val existingTx = txMap[msg.fromUser]?.get(msg.validCancel.transactionId) // TODO or QR
val existingTx: KotlinSasTransaction? =
getExistingTransaction(msg.validCancel.transactionId) // txMap[msg.fromUser]?.get(msg.validCancel.transactionId)
if (existingTx != null) { if (existingTx != null) {
existingTx.state = VerificationTxState.Cancelled(cancelCode, false) existingTx.state = SasTransactionState.Cancelled(cancelCode, false)
txMap[msg.fromUser]?.remove(msg.validCancel.transactionId) txMap[msg.fromUser]?.remove(msg.validCancel.transactionId)
eventFlow.emit(VerificationEvent.TransactionUpdated(existingTx)) eventFlow.emit(VerificationEvent.TransactionUpdated(existingTx))
} }
eventFlow.emit(VerificationEvent.RequestUpdated(request.toPendingVerificationRequest())) eventFlow.emit(VerificationEvent.RequestUpdated(request.toPendingVerificationRequest()))
} }
} }
is VerificationIntent.OnReadyByAnotherOfMySessionReceived -> { is VerificationIntent.OnReadyByAnotherOfMySessionReceived -> {
@ -358,6 +399,27 @@ internal class VerificationActor @AssistedInject constructor(
} }
private suspend fun handleReceiveStartForQR(request: KotlinVerificationRequest, reciprocate: ValidVerificationInfoStart.ReciprocateVerificationInfoStart) { private suspend fun handleReceiveStartForQR(request: KotlinVerificationRequest, reciprocate: ValidVerificationInfoStart.ReciprocateVerificationInfoStart) {
// Ok so the other did scan our code
val ourSecret = request.qrCodeData?.sharedSecret
if (ourSecret != reciprocate.sharedSecret) {
// something went wrong
cancelRequest(request, CancelCode.MismatchedKeys)
return
}
// The secret matches, we need manual action to confirm that it was scan
val tx = KotlinQRVerification(
channel = this.channel,
state = QRCodeVerificationState.WaitingForScanConfirmation,
qrCodeData = request.qrCodeData,
method = VerificationMethod.QR_CODE_SCAN,
transactionId = request.requestId,
otherUserId = request.otherUserId,
otherDeviceId = request.otherDeviceId(),
isIncoming = false,
isToDevice = request.roomId == null
)
addTransaction(tx)
} }
private suspend fun handleReceiveStartForSas( private suspend fun handleReceiveStartForSas(
@ -372,7 +434,7 @@ internal class VerificationActor @AssistedInject constructor(
// and the other m.key.verification.start event is ignored. // and the other m.key.verification.start event is ignored.
// So let's check if I already send a start? // So let's check if I already send a start?
val requestId = msg.validVerificationInfoStart.transactionId val requestId = msg.validVerificationInfoStart.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId) val existing: KotlinSasTransaction? = getExistingTransaction(msg.fromUser, requestId)
if (existing != null) { if (existing != null) {
Timber.tag(loggerTag.value) Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] No existing Sas transaction for ${request.requestId}") .v("[${myUserId.take(8)}] No existing Sas transaction for ${request.requestId}")
@ -380,23 +442,6 @@ internal class VerificationActor @AssistedInject constructor(
return return
} }
val sasTx = SasV1Transaction(
channel = channel,
transactionId = requestId,
state = VerificationTxState.None,
otherUserId = request.otherUserId,
myUserId = myUserId,
myTrustedMSK = cryptoStore.getMyCrossSigningInfo()
?.takeIf { it.isTrusted() }
?.masterKey()
?.unpaddedBase64PublicKey,
otherDeviceId = request.otherDeviceId(),
myDeviceId = cryptoStore.getDeviceId(),
myDeviceFingerprint = cryptoStore.getUserDevice(myUserId, cryptoStore.getDeviceId())?.fingerprint().orEmpty(),
startReq = sasStart,
isIncoming = true,
isToDevice = msg.viaRoom == null
)
// we accept with the agreement methods // we accept with the agreement methods
// Select a key agreement protocol, a hash algorithm, a message authentication code, // Select a key agreement protocol, a hash algorithm, a message authentication code,
// and short authentication string methods out of the lists given in requester's message. // and short authentication string methods out of the lists given in requester's message.
@ -440,11 +485,28 @@ internal class VerificationActor @AssistedInject constructor(
cancelRequest(request, CancelCode.UserError) cancelRequest(request, CancelCode.UserError)
return return
} }
val sasTx = KotlinSasTransaction(
channel = channel,
transactionId = requestId,
state = SasTransactionState.None,
otherUserId = request.otherUserId,
myUserId = myUserId,
myTrustedMSK = cryptoStore.getMyCrossSigningInfo()
?.takeIf { it.isTrusted() }
?.masterKey()
?.unpaddedBase64PublicKey,
otherDeviceId = request.otherDeviceId(),
myDeviceId = cryptoStore.getDeviceId(),
myDeviceFingerprint = cryptoStore.getUserDevice(myUserId, cryptoStore.getDeviceId())?.fingerprint().orEmpty(),
startReq = sasStart,
isIncoming = true,
isToDevice = msg.viaRoom == null,
)
val concat = sasTx.getSAS().publicKey + sasStart.canonicalJson val concat = sasTx.getSAS().publicKey + sasStart.canonicalJson
val commitment = hashUsingAgreedHashMethod(agreedHash, concat) val commitment = hashUsingAgreedHashMethod(agreedHash, concat)
val accept = SasV1Transaction.sasAccept( val accept = KotlinSasTransaction.sasAccept(
inRoom = request.roomId != null, inRoom = request.roomId != null,
requestId = requestId, requestId = requestId,
keyAgreementProtocol = agreedProtocol, keyAgreementProtocol = agreedProtocol,
@ -464,7 +526,6 @@ internal class VerificationActor @AssistedInject constructor(
} }
sasTx.accepted = accept.asValidObject() sasTx.accepted = accept.asValidObject()
sasTx.state = VerificationTxState.SasAccepted
addTransaction(sasTx) addTransaction(sasTx)
} }
@ -472,13 +533,13 @@ internal class VerificationActor @AssistedInject constructor(
private suspend fun handleReceiveAccept(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnAcceptReceived) { private suspend fun handleReceiveAccept(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnAcceptReceived) {
val requestId = msg.validAccept.transactionId val requestId = msg.validAccept.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId) val existing: KotlinSasTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also { ?: return Unit.also {
Timber.v("on accept received in room ${msg.viaRoom} for verification id:${requestId} in room ${matchingRequest.roomId}") Timber.v("on accept received in room ${msg.viaRoom} for verification id:${requestId} in room ${matchingRequest.roomId}")
} }
// Existing should be in // Existing should be in
if (existing.state != VerificationTxState.SasStarted) { if (existing.state != SasTransactionState.SasStarted) {
// it's a wrong state should cancel? // it's a wrong state should cancel?
// TODO cancel // TODO cancel
} }
@ -501,10 +562,9 @@ internal class VerificationActor @AssistedInject constructor(
// and replies with a to_device message with type set to “m.key.verification.key”, sending Alices public key QA // and replies with a to_device message with type set to “m.key.verification.key”, sending Alices public key QA
val pubKey = existing.getSAS().publicKey val pubKey = existing.getSAS().publicKey
val keyMessage = SasV1Transaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey) val keyMessage = KotlinSasTransaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey)
try { try {
if (BuildConfig.LOG_PRIVATE_DATA) { if (BuildConfig.LOG_PRIVATE_DATA) {
Timber.tag(loggerTag.value) Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}]: Sending my key $pubKey") .v("[${myUserId.take(8)}]: Sending my key $pubKey")
@ -515,7 +575,7 @@ internal class VerificationActor @AssistedInject constructor(
keyMessage, keyMessage,
) )
} catch (failure: Throwable) { } catch (failure: Throwable) {
existing.state = VerificationTxState.Cancelled(CancelCode.UserError, true) existing.state = SasTransactionState.Cancelled(CancelCode.UserError, true)
matchingRequest.cancelCode = CancelCode.UserError matchingRequest.cancelCode = CancelCode.UserError
matchingRequest.state = EVerificationState.Cancelled matchingRequest.state = EVerificationState.Cancelled
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest())) eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
@ -523,7 +583,7 @@ internal class VerificationActor @AssistedInject constructor(
return return
} }
existing.accepted = accept existing.accepted = accept
existing.state = VerificationTxState.SasKeySent existing.state = SasTransactionState.SasKeySent
eventFlow.emit(VerificationEvent.TransactionUpdated(existing)) eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
} }
@ -545,13 +605,13 @@ internal class VerificationActor @AssistedInject constructor(
msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Failed to find other device Id")) msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Failed to find other device Id"))
} }
val existingTransaction = getExistingTransaction(msg.otherUserId, msg.requestId) val existingTransaction = getExistingTransaction<VerificationTransaction>(msg.otherUserId, msg.requestId)
if (existingTransaction is SasVerificationTransaction) { if (existingTransaction is SasVerificationTransaction) {
// there is already an existing transaction?? // there is already an existing transaction??
msg.deferred.completeExceptionally(IllegalStateException("Already started")) msg.deferred.completeExceptionally(IllegalStateException("Already started"))
return return
} }
val startMessage = SasV1Transaction.sasStart( val startMessage = KotlinSasTransaction.sasStart(
inRoom = matchingRequest.roomId != null, inRoom = matchingRequest.roomId != null,
fromDevice = cryptoStore.getDeviceId(), fromDevice = cryptoStore.getDeviceId(),
requestId = msg.requestId requestId = msg.requestId
@ -564,10 +624,10 @@ internal class VerificationActor @AssistedInject constructor(
) )
// should check if already one (and cancel it) // should check if already one (and cancel it)
val tx = SasV1Transaction( val tx = KotlinSasTransaction(
channel = channel, channel = channel,
transactionId = msg.requestId, transactionId = msg.requestId,
state = VerificationTxState.SasStarted, state = SasTransactionState.SasStarted,
otherUserId = msg.otherUserId, otherUserId = msg.otherUserId,
myUserId = myUserId, myUserId = myUserId,
myTrustedMSK = cryptoStore.getMyCrossSigningInfo() myTrustedMSK = cryptoStore.getMyCrossSigningInfo()
@ -589,16 +649,22 @@ internal class VerificationActor @AssistedInject constructor(
msg.deferred.complete(tx) msg.deferred.complete(tx)
} }
private suspend fun handleReciprocateQR(msg: VerificationIntent.ActionReciprocateQrVerification) { private suspend fun handleActionReciprocateQR(msg: VerificationIntent.ActionReciprocateQrVerification) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] handle reciprocate for ${msg.requestId}")
val matchingRequest = pendingRequests val matchingRequest = pendingRequests
.flatMap { entry -> .flatMap { entry ->
entry.value.filter { it.requestId == msg.requestId } entry.value.filter { it.requestId == msg.requestId }
}.firstOrNull() }.firstOrNull()
?: return Unit.also { ?: return Unit.also {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] No matching request, abort ${msg.requestId}")
msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Unknown request")) msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Unknown request"))
} }
if (matchingRequest.state != EVerificationState.Ready) { if (matchingRequest.state != EVerificationState.Ready) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] Can't start if not ready, abort ${msg.requestId}")
msg.deferred.completeExceptionally(java.lang.IllegalStateException("Can't start a non ready request")) msg.deferred.completeExceptionally(java.lang.IllegalStateException("Can't start a non ready request"))
return return
} }
@ -607,21 +673,24 @@ internal class VerificationActor @AssistedInject constructor(
msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Failed to find other device Id")) msg.deferred.completeExceptionally(java.lang.IllegalArgumentException("Failed to find other device Id"))
} }
val existingTransaction = getExistingTransaction(msg.otherUserId, msg.requestId) val existingTransaction = getExistingTransaction<VerificationTransaction>(msg.otherUserId, msg.requestId)
// what if there is an existing?? // what if there is an existing??
if (existingTransaction != null) { if (existingTransaction != null) {
// cancel or replace?? // cancel or replace??
Timber.tag(loggerTag.value)
.w("[${myUserId.take(8)}] There is already a started transaction for request ${msg.requestId}")
return return
} }
val myMasterKey = crossSigningService.get() val myMasterKey = crossSigningService.get()
.getUserCrossSigningKeys(myUserId)?.masterKey()?.unpaddedBase64PublicKey .getUserCrossSigningKeys(myUserId)?.masterKey()?.unpaddedBase64PublicKey
var canTrustOtherUserMasterKey = false
// Check the other device view of my MSK // Check the other device view of my MSK
val otherQrCodeData = msg.scannedData.toQrCodeData() val otherQrCodeData = msg.scannedData.toQrCodeData()
when (otherQrCodeData) { when (otherQrCodeData) {
null -> { null -> {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] Malformed QR code ${msg.requestId}")
msg.deferred.completeExceptionally(IllegalArgumentException("Malformed QrCode data")) msg.deferred.completeExceptionally(IllegalArgumentException("Malformed QrCode data"))
return return
} }
@ -629,93 +698,85 @@ internal class VerificationActor @AssistedInject constructor(
// key2 (aka otherUserMasterCrossSigningPublicKey) is what the one displaying the QR code (other user) think my MSK is. // key2 (aka otherUserMasterCrossSigningPublicKey) is what the one displaying the QR code (other user) think my MSK is.
// Let's check that it's correct // Let's check that it's correct
// If not -> Cancel // If not -> Cancel
if (otherQrCodeData.otherUserMasterCrossSigningPublicKey != myMasterKey) { val whatOtherThinksMyMskIs = otherQrCodeData.otherUserMasterCrossSigningPublicKey
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}") if (whatOtherThinksMyMskIs != myMasterKey) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] ## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys) cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null) msg.deferred.complete(null)
return return
} else Unit }
val whatIThinkOtherMskIs = crossSigningService.get().getUserCrossSigningKeys(matchingRequest.otherUserId)
?.masterKey()
?.unpaddedBase64PublicKey
if (whatIThinkOtherMskIs != otherQrCodeData.userMasterCrossSigningPublicKey) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] ## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null)
return
}
} }
is QrCodeData.SelfVerifyingMasterKeyTrusted -> { is QrCodeData.SelfVerifyingMasterKeyTrusted -> {
if (matchingRequest.otherUserId != myUserId) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] Self mode qr with wrong user ${matchingRequest.otherUserId}")
cancelRequest(matchingRequest, CancelCode.MismatchedUser)
msg.deferred.complete(null)
return
}
// key1 (aka userMasterCrossSigningPublicKey) is the session displaying the QR code view of our MSK. // key1 (aka userMasterCrossSigningPublicKey) is the session displaying the QR code view of our MSK.
// Let's check that I see the same MSK // Let's check that I see the same MSK
// If not -> Cancel // If not -> Cancel
if (otherQrCodeData.userMasterCrossSigningPublicKey != myMasterKey) { val whatOtherThinksOurMskIs = otherQrCodeData.userMasterCrossSigningPublicKey
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") if (whatOtherThinksOurMskIs != myMasterKey) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] ## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null)
return
}
val whatOtherThinkMyDeviceKeyIs = otherQrCodeData.otherDeviceKey
val myDeviceKey = cryptoStore.getUserDevice(myUserId, cryptoStore.getDeviceId())?.fingerprint()
if (whatOtherThinkMyDeviceKeyIs != myDeviceKey) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] ## Verification QR: Invalid other device key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys) cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null) msg.deferred.complete(null)
return return
} else {
// I can trust the MSK then (i see the same one, and other session tell me it's trusted by him)
canTrustOtherUserMasterKey = true
} }
} }
is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> { is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> {
if (matchingRequest.otherUserId != myUserId) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] Self mode qr with wrong user ${matchingRequest.otherUserId}")
cancelRequest(matchingRequest, CancelCode.MismatchedUser)
msg.deferred.complete(null)
return
}
// key2 (aka userMasterCrossSigningPublicKey) is the session displaying the QR code view of our MSK. // key2 (aka userMasterCrossSigningPublicKey) is the session displaying the QR code view of our MSK.
// Let's check that it's the good one // Let's check that it's the good one
// If not -> Cancel // If not -> Cancel
if (otherQrCodeData.userMasterCrossSigningPublicKey != myMasterKey) { val otherDeclaredDeviceKey = otherQrCodeData.deviceKey
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}") val whatIThinkItIs = cryptoStore.getUserDevice(myUserId, otherDeviceId)?.fingerprint()
if (otherDeclaredDeviceKey != whatIThinkItIs) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] ## Verification QR: Invalid other device key $otherDeviceId")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys) cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null) msg.deferred.complete(null)
return
} else {
// Nothing special here, we will send a reciprocate start event, and then the other session will trust it's view of the MSK
}
}
} }
val toVerifyDeviceIds = mutableListOf<String>() val ownMasterKeyTrustedAsSeenByOther = otherQrCodeData.userMasterCrossSigningPublicKey
if (ownMasterKeyTrustedAsSeenByOther != myMasterKey) {
// Let's now check the other user/device key material Timber.tag(loggerTag.value)
when (otherQrCodeData) { .d("[${myUserId.take(8)}] ## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
is QrCodeData.VerifyingAnotherUser -> {
// key1(aka userMasterCrossSigningPublicKey) is the MSK of the one displaying the QR code (i.e other user)
// Let's check that it matches what I think it should be
if (otherQrCodeData.userMasterCrossSigningPublicKey
!= crossSigningService.get().getUserCrossSigningKeys(msg.otherUserId)?.masterKey()?.unpaddedBase64PublicKey) {
Timber.d("## Verification QR: Invalid user master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys) cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null) msg.deferred.complete(null)
return return
} else {
// It does so i should mark it as trusted
canTrustOtherUserMasterKey = true
} }
} }
is QrCodeData.SelfVerifyingMasterKeyTrusted -> {
// key2 (aka otherDeviceKey) is my current device key in POV of the one displaying the QR code (i.e other device)
// Let's check that it's correct
if (otherQrCodeData.otherDeviceKey
!= cryptoStore.getUserDevice(myUserId, cryptoStore.getDeviceId())?.fingerprint()) {
Timber.d("## Verification QR: Invalid other device key ${otherQrCodeData.otherDeviceKey}")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null)
return
} else Unit // Nothing special here, we will send a reciprocate start event, and then the other session will trust my device
// and thus allow me to request SSSS secret
}
is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> {
// key1 (aka otherDeviceKey) is the device key of the one displaying the QR code (i.e other device)
// Let's check that it matches what I have locally
if (otherQrCodeData.deviceKey
!= cryptoStore.getUserDevice(msg.otherUserId, otherDeviceId ?: "")?.fingerprint()) {
Timber.d("## Verification QR: Invalid device key ${otherQrCodeData.deviceKey}")
cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
msg.deferred.complete(null)
return
} else {
// Yes it does -> i should trust it and sign then upload the signature
toVerifyDeviceIds.add(otherDeviceId ?: "")
Unit
}
}
}
if (!canTrustOtherUserMasterKey && toVerifyDeviceIds.isEmpty()) {
// Nothing to verify
cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
return
} }
// All checks are correct // All checks are correct
@ -746,6 +807,8 @@ internal class VerificationActor @AssistedInject constructor(
try { try {
sendToOther(matchingRequest, EventType.KEY_VERIFICATION_START, message) sendToOther(matchingRequest, EventType.KEY_VERIFICATION_START, message)
} catch (failure: Throwable) { } catch (failure: Throwable) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] Failed to send reciprocate message")
msg.deferred.completeExceptionally(failure) msg.deferred.completeExceptionally(failure)
return return
} }
@ -754,21 +817,45 @@ internal class VerificationActor @AssistedInject constructor(
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest())) eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
val tx = KotlinQRVerification( val tx = KotlinQRVerification(
qrCodeText = msg.scannedData, channel = this.channel,
state = VerificationTxState.WaitingOtherReciprocateConfirm, state = QRCodeVerificationState.Reciprocated,
qrCodeData = msg.scannedData.toQrCodeData(),
method = VerificationMethod.QR_CODE_SCAN, method = VerificationMethod.QR_CODE_SCAN,
transactionId = msg.requestId, transactionId = msg.requestId,
otherUserId = msg.otherUserId, otherUserId = msg.otherUserId,
otherDeviceId = matchingRequest.otherDeviceId(), otherDeviceId = matchingRequest.otherDeviceId(),
isIncoming = false isIncoming = false,
isToDevice = matchingRequest.roomId == null
) )
addTransaction(tx) addTransaction(tx)
msg.deferred.complete(tx)
}
private suspend fun handleActionQRScanConfirmed(matchingRequest: KotlinVerificationRequest) {
val transaction = getExistingTransaction<KotlinQRVerification>(matchingRequest.otherUserId, matchingRequest.requestId)
if (transaction == null) {
// return
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}]: No matching transaction for key tId:${matchingRequest.requestId}")
return
}
if (transaction.state() == QRCodeVerificationState.WaitingForScanConfirmation) {
completeValidQRTransaction(transaction, matchingRequest)
} else {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}]: Unexpected confirm in state tId:${matchingRequest.requestId}")
// TODO throw?
cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
return
}
} }
private suspend fun handleReceiveKey(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnKeyReceived) { private suspend fun handleReceiveKey(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnKeyReceived) {
val requestId = msg.validKey.transactionId val requestId = msg.validKey.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId) val existing: KotlinSasTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also { ?: return Unit.also {
Timber.tag(loggerTag.value) Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}]: No matching transaction for key tId:$requestId") .v("[${myUserId.take(8)}]: No matching transaction for key tId:$requestId")
@ -776,9 +863,9 @@ internal class VerificationActor @AssistedInject constructor(
// Existing should be in SAS key sent // Existing should be in SAS key sent
val isCorrectState = if (existing.isIncoming) { val isCorrectState = if (existing.isIncoming) {
existing.state == VerificationTxState.SasAccepted existing.state == SasTransactionState.SasAccepted
} else { } else {
existing.state == VerificationTxState.SasKeySent existing.state == SasTransactionState.SasKeySent
} }
if (!isCorrectState) { if (!isCorrectState) {
@ -792,7 +879,7 @@ internal class VerificationActor @AssistedInject constructor(
if (existing.isIncoming) { if (existing.isIncoming) {
// ok i can now send my key and compute the sas code // ok i can now send my key and compute the sas code
val pubKey = existing.getSAS().publicKey val pubKey = existing.getSAS().publicKey
val keyMessage = SasV1Transaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey) val keyMessage = KotlinSasTransaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey)
try { try {
sendToOther( sendToOther(
matchingRequest, matchingRequest,
@ -804,7 +891,7 @@ internal class VerificationActor @AssistedInject constructor(
.v("[${myUserId.take(8)}]:i calculate SAS my key $pubKey their Key: $otherKey") .v("[${myUserId.take(8)}]:i calculate SAS my key $pubKey their Key: $otherKey")
} }
existing.calculateSASBytes(otherKey) existing.calculateSASBytes(otherKey)
existing.state = VerificationTxState.SasShortCodeReady existing.state = SasTransactionState.SasShortCodeReady
if (BuildConfig.LOG_PRIVATE_DATA) { if (BuildConfig.LOG_PRIVATE_DATA) {
Timber.tag(loggerTag.value) Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}]:i CODE ${existing.getDecimalCodeRepresentation()}") .v("[${myUserId.take(8)}]:i CODE ${existing.getDecimalCodeRepresentation()}")
@ -813,7 +900,7 @@ internal class VerificationActor @AssistedInject constructor(
} }
eventFlow.emit(VerificationEvent.TransactionUpdated(existing)) eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
} catch (failure: Throwable) { } catch (failure: Throwable) {
existing.state = VerificationTxState.Cancelled(CancelCode.UserError, true) existing.state = SasTransactionState.Cancelled(CancelCode.UserError, true)
matchingRequest.state = EVerificationState.Cancelled matchingRequest.state = EVerificationState.Cancelled
matchingRequest.cancelCode = CancelCode.UserError matchingRequest.cancelCode = CancelCode.UserError
eventFlow.emit(VerificationEvent.TransactionUpdated(existing)) eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
@ -843,7 +930,7 @@ internal class VerificationActor @AssistedInject constructor(
.v("[${myUserId.take(8)}]:o calculate SAS my key ${existing.getSAS().publicKey} their Key: $otherKey") .v("[${myUserId.take(8)}]:o calculate SAS my key ${existing.getSAS().publicKey} their Key: $otherKey")
} }
existing.calculateSASBytes(otherKey) existing.calculateSASBytes(otherKey)
existing.state = VerificationTxState.SasShortCodeReady existing.state = SasTransactionState.SasShortCodeReady
eventFlow.emit(VerificationEvent.TransactionUpdated(existing)) eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
if (BuildConfig.LOG_PRIVATE_DATA) { if (BuildConfig.LOG_PRIVATE_DATA) {
Timber.tag(loggerTag.value) Timber.tag(loggerTag.value)
@ -864,21 +951,21 @@ internal class VerificationActor @AssistedInject constructor(
private suspend fun handleMacReceived(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnMacReceived) { private suspend fun handleMacReceived(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnMacReceived) {
val requestId = msg.validMac.transactionId val requestId = msg.validMac.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId) val existing: KotlinSasTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also { ?: return Unit.also {
Timber.tag(loggerTag.value) Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] on Mac for unknown transaction with id:$requestId") .v("[${myUserId.take(8)}] on Mac for unknown transaction with id:$requestId")
} }
when (existing.state) { when (existing.state) {
is VerificationTxState.SasMacSent -> { is SasTransactionState.SasMacSent -> {
existing.theirMac = msg.validMac existing.theirMac = msg.validMac
finalizeSasTransaction(existing, msg.validMac, matchingRequest, existing.transactionId) finalizeSasTransaction(existing, msg.validMac, matchingRequest, existing.transactionId)
} }
is VerificationTxState.SasShortCodeReady -> { is SasTransactionState.SasShortCodeReady -> {
// I can start verify, store it // I can start verify, store it
existing.theirMac = msg.validMac existing.theirMac = msg.validMac
existing.state = VerificationTxState.SasMacReceived(false) existing.state = SasTransactionState.SasMacReceived(false)
eventFlow.emit(VerificationEvent.TransactionUpdated(existing)) eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
} }
else -> { else -> {
@ -901,14 +988,14 @@ internal class VerificationActor @AssistedInject constructor(
msg.deferred.completeExceptionally(IllegalStateException("Request was cancelled")) msg.deferred.completeExceptionally(IllegalStateException("Request was cancelled"))
} }
} }
val existing = getExistingTransaction(transactionId) val existing: KotlinSasTransaction = getExistingTransaction(transactionId)
?: return Unit.also { ?: return Unit.also {
msg.deferred.completeExceptionally(IllegalStateException("Unknown Transaction")) msg.deferred.completeExceptionally(IllegalStateException("Unknown Transaction"))
} }
val isCorrectState = when (val state = existing.state) { val isCorrectState = when (val state = existing.state) {
is VerificationTxState.SasShortCodeReady -> true is SasTransactionState.SasShortCodeReady -> true
is VerificationTxState.SasMacReceived -> !state.codeConfirmed is SasTransactionState.SasMacReceived -> !state.codeConfirmed
else -> false else -> false
} }
if (!isCorrectState) { if (!isCorrectState) {
@ -927,25 +1014,122 @@ internal class VerificationActor @AssistedInject constructor(
private suspend fun handleDoneReceived(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnDoneReceived) { private suspend fun handleDoneReceived(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnDoneReceived) {
val requestId = msg.transactionId val requestId = msg.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId) val existing: VerificationTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also { ?: return Unit.also {
Timber.v("on accept received in room ${msg.viaRoom} for verification id:${requestId} in room ${matchingRequest.roomId}") Timber.v("on accept received in room ${msg.viaRoom} for verification id:${requestId} in room ${matchingRequest.roomId}")
} }
when {
existing is KotlinSasTransaction -> {
val state = existing.state val state = existing.state
val isCorrectState = state is VerificationTxState.Done && !state.otherDone val isCorrectState = state is SasTransactionState.Done && !state.otherDone
if (isCorrectState) { if (isCorrectState) {
existing.state = SasTransactionState.Done(true)
eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
// we can forget about it
txMap[matchingRequest.otherUserId()]?.remove(matchingRequest.requestId)
// XXX whatabout waiting for done? // XXX whatabout waiting for done?
matchingRequest.state = EVerificationState.Done matchingRequest.state = EVerificationState.Done
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest())) eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
// updatePendingRequest(
// matchingRequest.copy(
// isSuccessful = true
// )
// )
} else { } else {
// TODO cancel? // TODO cancel?
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}]: Unexpected done in state $state")
cancelRequest(matchingRequest, CancelCode.UnexpectedMessage)
}
}
existing is KotlinQRVerification -> {
val state = existing.state()
when (state) {
QRCodeVerificationState.Reciprocated -> {
completeValidQRTransaction(existing, matchingRequest)
}
QRCodeVerificationState.WaitingForOtherDone -> {
matchingRequest.state = EVerificationState.Done
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
}
else -> {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}]: Unexpected done in state $state")
cancelRequest(matchingRequest, CancelCode.UnexpectedMessage)
}
}
}
else -> {
// unexpected message?
cancelRequest(matchingRequest, CancelCode.UnexpectedMessage)
}
}
}
private suspend fun completeValidQRTransaction(existing: KotlinQRVerification, matchingRequest: KotlinVerificationRequest) {
var shouldRequestSecret = false
// Ok so the other side is fine let's trust what we need to trust
when (existing.qrCodeData) {
is QrCodeData.VerifyingAnotherUser -> {
// let's trust him
// it's his code scanned so user is him and other me
try {
crossSigningService.get().trustUser(matchingRequest.otherUserId)
} catch (failure: Throwable) {
// fail silently?
// at least it will be marked as trusted locally?
}
}
is QrCodeData.SelfVerifyingMasterKeyNotTrusted -> {
// the other device is the one that doesn't trust yet our MSK
// As all is good I can upload a signature for my new device
// Also notify the secret share manager for the soon to come secret share requests
secretShareManager.onVerificationCompleteForDevice(matchingRequest.otherDeviceId()!!)
try {
crossSigningService.get().trustDevice(matchingRequest.otherDeviceId()!!)
} catch (failure: Throwable) {
// network problem??
Timber.w("## Verification: Failed to sign new device ${matchingRequest.otherDeviceId()}, ${failure.localizedMessage}")
}
}
is QrCodeData.SelfVerifyingMasterKeyTrusted -> {
// I can trust my MSK
crossSigningService.get().markMyMasterKeyAsTrusted()
shouldRequestSecret = true
}
null -> {
// This shouldn't happen? cancel?
}
}
sendToOther(
matchingRequest,
EventType.KEY_VERIFICATION_DONE,
if (matchingRequest.roomId != null) {
MessageVerificationDoneContent(
relatesTo = RelationDefaultContent(
RelationType.REFERENCE,
matchingRequest.requestId
)
)
} else {
KeyVerificationDone(matchingRequest.requestId)
}
)
existing.state = QRCodeVerificationState.Done
eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
// we can forget about it
txMap[matchingRequest.otherUserId()]?.remove(matchingRequest.requestId)
matchingRequest.state = EVerificationState.WaitingForDone
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
if (shouldRequestSecret) {
matchingRequest.otherDeviceId()?.let { otherDeviceId ->
secretShareManager.requestSecretTo(otherDeviceId, MASTER_KEY_SSSS_NAME)
secretShareManager.requestSecretTo(otherDeviceId, SELF_SIGNING_KEY_SSSS_NAME)
secretShareManager.requestSecretTo(otherDeviceId, USER_SIGNING_KEY_SSSS_NAME)
secretShareManager.requestSecretTo(otherDeviceId, KEYBACKUP_SECRET_SSSS_NAME)
}
} }
} }
@ -956,21 +1140,21 @@ internal class VerificationActor @AssistedInject constructor(
msg.deferred.completeExceptionally(IllegalStateException("Unknown Request")) msg.deferred.completeExceptionally(IllegalStateException("Unknown Request"))
} }
if (matchingRequest.state != EVerificationState.WeStarted if (matchingRequest.state != EVerificationState.WeStarted &&
&& matchingRequest.state != EVerificationState.Started) { matchingRequest.state != EVerificationState.Started) {
return Unit.also { return Unit.also {
msg.deferred.completeExceptionally(IllegalStateException("Can't accept code in state: ${matchingRequest.state}")) msg.deferred.completeExceptionally(IllegalStateException("Can't accept code in state: ${matchingRequest.state}"))
} }
} }
val existing = getExistingTransaction(transactionId) val existing: KotlinSasTransaction = getExistingTransaction(transactionId)
?: return Unit.also { ?: return Unit.also {
msg.deferred.completeExceptionally(IllegalStateException("Unknown Transaction")) msg.deferred.completeExceptionally(IllegalStateException("Unknown Transaction"))
} }
val isCorrectState = when (val state = existing.state) { val isCorrectState = when (val state = existing.state) {
is VerificationTxState.SasShortCodeReady -> true is SasTransactionState.SasShortCodeReady -> true
is VerificationTxState.SasMacReceived -> !state.codeConfirmed is SasTransactionState.SasMacReceived -> !state.codeConfirmed
else -> false else -> false
} }
if (!isCorrectState) { if (!isCorrectState) {
@ -981,7 +1165,7 @@ internal class VerificationActor @AssistedInject constructor(
val macInfo = existing.computeMyMac() val macInfo = existing.computeMyMac()
val macMsg = SasV1Transaction.sasMacMessage(matchingRequest.roomId != null, transactionId, macInfo) val macMsg = KotlinSasTransaction.sasMacMessage(matchingRequest.roomId != null, transactionId, macInfo)
try { try {
sendToOther(matchingRequest, EventType.KEY_VERIFICATION_MAC, macMsg) sendToOther(matchingRequest, EventType.KEY_VERIFICATION_MAC, macMsg)
} catch (failure: Throwable) { } catch (failure: Throwable) {
@ -995,7 +1179,7 @@ internal class VerificationActor @AssistedInject constructor(
if (theirMac != null) { if (theirMac != null) {
finalizeSasTransaction(existing, theirMac, matchingRequest, transactionId) finalizeSasTransaction(existing, theirMac, matchingRequest, transactionId)
} else { } else {
existing.state = VerificationTxState.SasMacSent existing.state = SasTransactionState.SasMacSent
eventFlow.emit(VerificationEvent.TransactionUpdated(existing)) eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
} }
@ -1003,7 +1187,7 @@ internal class VerificationActor @AssistedInject constructor(
} }
private suspend fun finalizeSasTransaction( private suspend fun finalizeSasTransaction(
existing: SasV1Transaction, existing: KotlinSasTransaction,
theirMac: ValidVerificationInfoMac, theirMac: ValidVerificationInfoMac,
matchingRequest: KotlinVerificationRequest, matchingRequest: KotlinVerificationRequest,
transactionId: String transactionId: String
@ -1017,7 +1201,7 @@ internal class VerificationActor @AssistedInject constructor(
Timber.tag(loggerTag.value) Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] verify macs result $result id:$transactionId") .v("[${myUserId.take(8)}] verify macs result $result id:$transactionId")
when (result) { when (result) {
is SasV1Transaction.MacVerificationResult.Success -> { is KotlinSasTransaction.MacVerificationResult.Success -> {
// mark the devices as locally trusted // mark the devices as locally trusted
result.verifiedDeviceId.forEach { deviceId -> result.verifiedDeviceId.forEach { deviceId ->
val actualTrustLevel = cryptoStore.getUserDevice(matchingRequest.otherUserId, deviceId)?.trustLevel val actualTrustLevel = cryptoStore.getUserDevice(matchingRequest.otherUserId, deviceId)?.trustLevel
@ -1068,17 +1252,17 @@ internal class VerificationActor @AssistedInject constructor(
} }
) )
existing.state = VerificationTxState.Done(false) existing.state = SasTransactionState.Done(false)
eventFlow.emit(VerificationEvent.TransactionUpdated(existing)) eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
pastTransactions.getOrPut(transactionId) { mutableMapOf() }[transactionId] = existing pastTransactions.getOrPut(transactionId) { mutableMapOf() }[transactionId] = existing
txMap[matchingRequest.otherUserId]?.remove(transactionId) txMap[matchingRequest.otherUserId]?.remove(transactionId)
matchingRequest.state = EVerificationState.WaitingForDone matchingRequest.state = EVerificationState.WaitingForDone
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest())) eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
} }
SasV1Transaction.MacVerificationResult.MismatchKeys, KotlinSasTransaction.MacVerificationResult.MismatchKeys,
SasV1Transaction.MacVerificationResult.MismatchMacCrossSigning, KotlinSasTransaction.MacVerificationResult.MismatchMacCrossSigning,
is SasV1Transaction.MacVerificationResult.MismatchMacDevice, is KotlinSasTransaction.MacVerificationResult.MismatchMacDevice,
SasV1Transaction.MacVerificationResult.NoDevicesVerified -> { KotlinSasTransaction.MacVerificationResult.NoDevicesVerified -> {
cancelRequest(matchingRequest, CancelCode.MismatchedKeys) cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
} }
} }
@ -1126,7 +1310,7 @@ internal class VerificationActor @AssistedInject constructor(
commonMethods commonMethods
) )
val message = SasV1Transaction.sasReady( val message = KotlinSasTransaction.sasReady(
inRoom = existing.roomId != null, inRoom = existing.roomId != null,
requestId = msg.transactionId, requestId = msg.transactionId,
methods = commonMethods, methods = commonMethods,
@ -1533,11 +1717,17 @@ internal class VerificationActor @AssistedInject constructor(
eventFlow.emit(VerificationEvent.RequestUpdated(request.toPendingVerificationRequest())) eventFlow.emit(VerificationEvent.RequestUpdated(request.toPendingVerificationRequest()))
// should also update SAS/QR transaction // should also update SAS/QR transaction
getExistingTransaction(request.otherUserId, request.requestId)?.let { getExistingTransaction<KotlinSasTransaction>(request.otherUserId, request.requestId)?.let {
it.state = VerificationTxState.Cancelled(code, true) it.state = SasTransactionState.Cancelled(code, true)
txMap[request.otherUserId]?.remove(request.requestId) txMap[request.otherUserId]?.remove(request.requestId)
eventFlow.emit(VerificationEvent.TransactionUpdated(it)) eventFlow.emit(VerificationEvent.TransactionUpdated(it))
} }
getExistingTransaction<KotlinQRVerification>(request.otherUserId, request.requestId)?.let {
it.state = QRCodeVerificationState.Cancelled
txMap[request.otherUserId]?.remove(request.requestId)
eventFlow.emit(VerificationEvent.TransactionUpdated(it))
}
cancelRequest(request.requestId, request.roomId, request.otherUserId, request.otherDeviceId(), code) cancelRequest(request.requestId, request.roomId, request.otherUserId, request.otherDeviceId(), code)
} }
@ -1592,17 +1782,17 @@ internal class VerificationActor @AssistedInject constructor(
throw java.lang.IllegalArgumentException("Unsupported hash method $hashMethod") throw java.lang.IllegalArgumentException("Unsupported hash method $hashMethod")
} }
private suspend fun addTransaction(tx: SasV1Transaction) { private suspend fun addTransaction(tx: VerificationTransaction) {
val txInnerMap = txMap.getOrPut(tx.otherUserId) { mutableMapOf() } val txInnerMap = txMap.getOrPut(tx.otherUserId) { mutableMapOf() }
txInnerMap[tx.transactionId] = tx txInnerMap[tx.transactionId] = tx
eventFlow.emit(VerificationEvent.TransactionAdded(tx)) eventFlow.emit(VerificationEvent.TransactionAdded(tx))
} }
private fun getExistingTransaction(otherUserId: String, transactionId: String): SasV1Transaction? { private inline fun <reified T : VerificationTransaction> getExistingTransaction(otherUserId: String, transactionId: String): T? {
return txMap[otherUserId]?.get(transactionId) return txMap[otherUserId]?.get(transactionId) as? T
} }
private inline fun <reified T: VerificationTransaction> getExistingTransaction(transactionId: String, type: T): T? { private inline fun <reified T : VerificationTransaction> getExistingTransaction(transactionId: String): T? {
txMap.forEach { txMap.forEach {
val match = it.value.values val match = it.value.values
.firstOrNull { it.transactionId == transactionId } .firstOrNull { it.transactionId == transactionId }

View File

@ -17,7 +17,6 @@
package org.matrix.android.sdk.internal.crypto.verification package org.matrix.android.sdk.internal.crypto.verification
import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CompletableDeferred
import org.matrix.android.sdk.api.session.crypto.verification.IVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoReady
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
@ -106,6 +105,12 @@ internal sealed class VerificationIntent {
val deferred: CompletableDeferred<VerificationTransaction?>, val deferred: CompletableDeferred<VerificationTransaction?>,
) : VerificationIntent() ) : VerificationIntent()
data class ActionConfirmCodeWasScanned(
val otherUserId: String,
val requestId: String,
val deferred: CompletableDeferred<Unit>,
) : VerificationIntent()
data class OnStartReceived( data class OnStartReceived(
val viaRoom: String?, val viaRoom: String?,
val fromUser: String, val fromUser: String,

View File

@ -23,8 +23,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageContent
import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent import org.matrix.android.sdk.api.session.room.model.message.MessageRelationContent
import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageType
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationReadyContent import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationReadyContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent
import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationStartContent
import org.matrix.android.sdk.internal.di.DeviceId import org.matrix.android.sdk.internal.di.DeviceId
import org.matrix.android.sdk.internal.di.UserId import org.matrix.android.sdk.internal.di.UserId
import org.matrix.android.sdk.internal.util.time.Clock import org.matrix.android.sdk.internal.util.time.Clock
@ -56,7 +54,7 @@ internal class VerificationMessageProcessor @Inject constructor(
return allowedTypes.contains(eventType) return allowedTypes.contains(eventType)
} }
suspend fun process(roomId:String, event: Event) { suspend fun process(roomId: String, event: Event) {
Timber.v("## SAS Verification live observer: received msgId: ${event.eventId} msgtype: ${event.getClearType()} from ${event.senderId}") Timber.v("## SAS Verification live observer: received msgId: ${event.eventId} msgtype: ${event.getClearType()} from ${event.senderId}")
// If the request is in the future by more than 5 minutes or more than 10 minutes in the past, // If the request is in the future by more than 5 minutes or more than 10 minutes in the past,

View File

@ -16,13 +16,13 @@
package org.matrix.android.sdk.api.session.crypto.verification package org.matrix.android.sdk.api.session.crypto.verification
import org.matrix.android.sdk.internal.crypto.verification.KotlinVerificationRequest
enum class EVerificationState { enum class EVerificationState {
// outgoing started request // outgoing started request
WaitingForReady, WaitingForReady,
// for incoming // for incoming
Requested, Requested,
// both incoming/outgoing // both incoming/outgoing
Ready, Ready,
Started, Started,
@ -34,15 +34,16 @@ enum class EVerificationState {
} }
// TODO remove that // TODO remove that
interface IVerificationRequest{ interface IVerificationRequest {
fun requestId(): String fun requestId(): String
fun incoming(): Boolean fun incoming(): Boolean
fun otherUserId(): String fun otherUserId(): String
fun roomId(): String? fun roomId(): String?
// target devices in case of to_device self verification // target devices in case of to_device self verification
fun targetDevices() : List<String>? fun targetDevices(): List<String>?
fun state(): EVerificationState fun state(): EVerificationState
fun ageLocalTs(): Long fun ageLocalTs(): Long
@ -53,10 +54,9 @@ interface IVerificationRequest{
fun otherDeviceId(): String? fun otherDeviceId(): String?
fun qrCodeText() : String? fun qrCodeText(): String?
fun isFinished() : Boolean = state() == EVerificationState.Cancelled || state() == EVerificationState.Done fun isFinished(): Boolean = state() == EVerificationState.Cancelled || state() == EVerificationState.Done
fun cancelCode(): CancelCode? fun cancelCode(): CancelCode?
} }

View File

@ -15,13 +15,6 @@
*/ */
package org.matrix.android.sdk.api.session.crypto.verification package org.matrix.android.sdk.api.session.crypto.verification
import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW
import org.matrix.android.sdk.internal.crypto.model.rest.VERIFICATION_METHOD_SAS
import java.util.UUID
/** /**
* Stores current pending verification requests. * Stores current pending verification requests.
*/ */
@ -34,7 +27,7 @@ data class PendingVerificationRequest(
val otherDeviceId: String?, val otherDeviceId: String?,
// in case of verification via room, it will be not null // in case of verification via room, it will be not null
val roomId: String?, val roomId: String?,
val transactionId: String,//? = null, val transactionId: String, // ? = null,
// val requestInfo: ValidVerificationInfoRequest? = null, // val requestInfo: ValidVerificationInfoRequest? = null,
// val readyInfo: ValidVerificationInfoReady? = null, // val readyInfo: ValidVerificationInfoReady? = null,
val cancelConclusion: CancelCode? = null, val cancelConclusion: CancelCode? = null,
@ -52,5 +45,4 @@ data class PendingVerificationRequest(
// val isReady: Boolean = readyInfo != null // val isReady: Boolean = readyInfo != null
// //
// val isFinished: Boolean = isSuccessful || cancelConclusion != null // val isFinished: Boolean = isSuccessful || cancelConclusion != null
} }

View File

@ -16,8 +16,22 @@
package org.matrix.android.sdk.api.session.crypto.verification package org.matrix.android.sdk.api.session.crypto.verification
enum class QRCodeVerificationState {
// ie. we started
Reciprocated,
// When started/scanned by other side and waiting for confirmation
// that was really scanned
WaitingForScanConfirmation,
WaitingForOtherDone,
Done,
Cancelled
}
interface QrCodeVerificationTransaction : VerificationTransaction { interface QrCodeVerificationTransaction : VerificationTransaction {
fun state(): QRCodeVerificationState
/** /**
* To use to display a qr code, for the other user to scan it. * To use to display a qr code, for the other user to scan it.
*/ */
@ -26,7 +40,7 @@ interface QrCodeVerificationTransaction : VerificationTransaction {
/** /**
* Call when you have scan the other user QR code. * Call when you have scan the other user QR code.
*/ */
suspend fun userHasScannedOtherQrCode(otherQrCodeText: String) // suspend fun userHasScannedOtherQrCode(otherQrCodeText: String)
/** /**
* Call when you confirm that other user has scanned your QR code. * Call when you confirm that other user has scanned your QR code.
@ -37,4 +51,6 @@ interface QrCodeVerificationTransaction : VerificationTransaction {
* Call when you do not confirm that other user has scanned your QR code. * Call when you do not confirm that other user has scanned your QR code.
*/ */
suspend fun otherUserDidNotScannedMyQrCode() suspend fun otherUserDidNotScannedMyQrCode()
override fun isSuccessful() = state() == QRCodeVerificationState.Done
} }

View File

@ -40,6 +40,10 @@ interface SasVerificationTransaction : VerificationTransaction {
val KNOWN_SHORT_CODES = listOf(SasMode.EMOJI, SasMode.DECIMAL) val KNOWN_SHORT_CODES = listOf(SasMode.EMOJI, SasMode.DECIMAL)
} }
fun state(): SasTransactionState
override fun isSuccessful() = state() is SasTransactionState.Done
fun supportsEmoji(): Boolean fun supportsEmoji(): Boolean
fun getEmojiCodeRepresentation(): List<EmojiRepresentation> fun getEmojiCodeRepresentation(): List<EmojiRepresentation>

View File

@ -23,8 +23,8 @@ sealed class VerificationEvent(val transactionId: String) {
data class TransactionUpdated(val transaction: VerificationTransaction) : VerificationEvent(transaction.transactionId) data class TransactionUpdated(val transaction: VerificationTransaction) : VerificationEvent(transaction.transactionId)
} }
fun VerificationEvent.getRequest() : PendingVerificationRequest? { fun VerificationEvent.getRequest(): PendingVerificationRequest? {
return when(this) { return when (this) {
is VerificationEvent.RequestAdded -> this.request is VerificationEvent.RequestAdded -> this.request
is VerificationEvent.RequestUpdated -> this.request is VerificationEvent.RequestUpdated -> this.request
is VerificationEvent.TransactionAdded -> null is VerificationEvent.TransactionAdded -> null
@ -32,12 +32,11 @@ fun VerificationEvent.getRequest() : PendingVerificationRequest? {
} }
} }
fun VerificationEvent.getTransaction() : VerificationTransaction? { fun VerificationEvent.getTransaction(): VerificationTransaction? {
return when(this) { return when (this) {
is VerificationEvent.RequestAdded -> null is VerificationEvent.RequestAdded -> null
is VerificationEvent.RequestUpdated -> null is VerificationEvent.RequestUpdated -> null
is VerificationEvent.TransactionAdded -> this.transaction is VerificationEvent.TransactionAdded -> this.transaction
is VerificationEvent.TransactionUpdated -> this.transaction is VerificationEvent.TransactionUpdated -> this.transaction
} }
} }

View File

@ -18,8 +18,6 @@ package org.matrix.android.sdk.api.session.crypto.verification
interface VerificationTransaction { interface VerificationTransaction {
val state: VerificationTxState
val method: VerificationMethod val method: VerificationMethod
val transactionId: String val transactionId: String
@ -37,4 +35,6 @@ interface VerificationTransaction {
suspend fun cancel(code: CancelCode) suspend fun cancel(code: CancelCode)
fun isToDeviceTransport(): Boolean fun isToDeviceTransport(): Boolean
fun isSuccessful(): Boolean
} }

View File

@ -16,6 +16,35 @@
package org.matrix.android.sdk.api.session.crypto.verification package org.matrix.android.sdk.api.session.crypto.verification
sealed class SasTransactionState {
object None : SasTransactionState()
// I wend a start
object SasStarted : SasTransactionState()
// I received a start and it was accepted
object SasAccepted : SasTransactionState()
// I received an accept and sent my key
object SasKeySent : SasTransactionState()
// Keys exchanged and code ready to be shared
object SasShortCodeReady : SasTransactionState()
// I received the other Mac, but might have not yet confirmed the short code
// at that time (other side already confirmed)
data class SasMacReceived(val codeConfirmed: Boolean) : SasTransactionState()
// I confirmed the code and sent my mac
object SasMacSent : SasTransactionState()
// I am done, waiting for other Done
data class Done(val otherDone: Boolean) : SasTransactionState()
data class Cancelled(val cancelCode: CancelCode, val byMe: Boolean) : SasTransactionState()
}
sealed class VerificationTxState { sealed class VerificationTxState {
/** /**
* Uninitialized state. * Uninitialized state.
@ -42,24 +71,24 @@ sealed class VerificationTxState {
// object MacSent : VerificationSasTxState() // object MacSent : VerificationSasTxState()
// object Verifying : VerificationSasTxState() // object Verifying : VerificationSasTxState()
// I wend a start // // I wend a start
object SasStarted : VerificationSasTxState() // object SasStarted : VerificationSasTxState()
//
// I received a start and it was accepted // // I received a start and it was accepted
object SasAccepted : VerificationSasTxState() // object SasAccepted : VerificationSasTxState()
//
// I received an accept and sent my key // // I received an accept and sent my key
object SasKeySent : VerificationSasTxState() // object SasKeySent : VerificationSasTxState()
//
// Keys exchanged and code ready to be shared // // Keys exchanged and code ready to be shared
object SasShortCodeReady : VerificationSasTxState() // object SasShortCodeReady : VerificationSasTxState()
//
// I received the other Mac, but might have not yet confirmed the short code // // I received the other Mac, but might have not yet confirmed the short code
// at that time (other side already confirmed) // // at that time (other side already confirmed)
data class SasMacReceived(val codeConfirmed: Boolean) : VerificationSasTxState() // data class SasMacReceived(val codeConfirmed: Boolean) : VerificationSasTxState()
//
// I confirmed the code and sent my mac // // I confirmed the code and sent my mac
object SasMacSent : VerificationSasTxState() // object SasMacSent : VerificationSasTxState()
// I am done, waiting for other Done // I am done, waiting for other Done
data class Done(val otherDone: Boolean) : VerificationSasTxState() data class Done(val otherDone: Boolean) : VerificationSasTxState()
@ -67,13 +96,13 @@ sealed class VerificationTxState {
/** /**
* Specific for QR code. * Specific for QR code.
*/ */
abstract class VerificationQrTxState : VerificationTxState() // abstract class VerificationQrTxState : VerificationTxState()
//
/** // /**
* Will be used to ask the user if the other user has correctly scanned. // * Will be used to ask the user if the other user has correctly scanned.
*/ // */
object QrScannedByOther : VerificationQrTxState() // object QrScannedByOther : VerificationQrTxState()
object WaitingOtherReciprocateConfirm : VerificationQrTxState() // object WaitingOtherReciprocateConfirm : VerificationQrTxState()
/** /**
* Terminal states. * Terminal states.

View File

@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import org.matrix.android.sdk.api.session.crypto.verification.IVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction

View File

@ -31,9 +31,6 @@ import im.vector.app.features.createdirect.CreateDirectRoomViewModel
import im.vector.app.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel import im.vector.app.features.crypto.keysbackup.settings.KeysBackupSettingsViewModel
import im.vector.app.features.crypto.quads.SharedSecureStorageViewModel import im.vector.app.features.crypto.quads.SharedSecureStorageViewModel
import im.vector.app.features.crypto.recover.BootstrapSharedViewModel import im.vector.app.features.crypto.recover.BootstrapSharedViewModel
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodViewModel
import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeViewModel
import im.vector.app.features.crypto.verification.user.UserVerificationViewModel import im.vector.app.features.crypto.verification.user.UserVerificationViewModel
import im.vector.app.features.devtools.RoomDevToolViewModel import im.vector.app.features.devtools.RoomDevToolViewModel
import im.vector.app.features.discovery.DiscoverySettingsViewModel import im.vector.app.features.discovery.DiscoverySettingsViewModel
@ -512,15 +509,10 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(MessageActionsViewModel::class) @MavericksViewModelKey(MessageActionsViewModel::class)
fun messageActionsViewModelFactory(factory: MessageActionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *> fun messageActionsViewModelFactory(factory: MessageActionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds // @Binds
@IntoMap // @IntoMap
@MavericksViewModelKey(VerificationChooseMethodViewModel::class) // @MavericksViewModelKey(VerificationChooseMethodViewModel::class)
fun verificationChooseMethodViewModelFactory(factory: VerificationChooseMethodViewModel.Factory): MavericksAssistedViewModelFactory<*, *> // fun verificationChooseMethodViewModelFactory(factory: VerificationChooseMethodViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(VerificationEmojiCodeViewModel::class)
fun verificationEmojiCodeViewModelFactory(factory: VerificationEmojiCodeViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds @Binds
@IntoMap @IntoMap
@ -592,11 +584,10 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(BootstrapSharedViewModel::class) @MavericksViewModelKey(BootstrapSharedViewModel::class)
fun bootstrapSharedViewModelFactory(factory: BootstrapSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *> fun bootstrapSharedViewModelFactory(factory: BootstrapSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds // @Binds
@IntoMap // @IntoMap
@MavericksViewModelKey(VerificationBottomSheetViewModel::class) // @MavericksViewModelKey(VerificationBottomSheetViewModel::class)
fun verificationBottomSheetViewModelFactory(factory: VerificationBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *> // fun verificationBottomSheetViewModelFactory(factory: VerificationBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds @Binds
@IntoMap @IntoMap

View File

@ -25,7 +25,6 @@ import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.session.coroutineScope import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel import kotlinx.coroutines.cancel
import kotlinx.coroutines.flow.cancellable import kotlinx.coroutines.flow.cancellable
@ -39,11 +38,11 @@ import org.matrix.android.sdk.api.session.crypto.model.CryptoDeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest import org.matrix.android.sdk.api.session.crypto.model.IncomingRoomKeyRequest
import org.matrix.android.sdk.api.session.crypto.model.SecretShareRequest import org.matrix.android.sdk.api.session.crypto.model.SecretShareRequest
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
@ -275,8 +274,8 @@ class KeyRequestHandler @Inject constructor(
override fun transactionUpdated(tx: VerificationTransaction) { override fun transactionUpdated(tx: VerificationTransaction) {
if (tx is SasVerificationTransaction) { if (tx is SasVerificationTransaction) {
val state = tx.state val state = tx.state()
if (state == VerificationTxState.Verified) { if (state is SasTransactionState.Done) {
// ok it's verified, see if we have key request for that // ok it's verified, see if we have key request for that
shareAllSessions("${tx.otherDeviceId}${tx.otherUserId}") shareAllSessions("${tx.otherDeviceId}${tx.otherUserId}")
popupAlertManager.cancelAlert("ikr_${tx.otherDeviceId}${tx.otherUserId}") popupAlertManager.cancelAlert("ikr_${tx.otherDeviceId}${tx.otherUserId}")

View File

@ -37,10 +37,11 @@ import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.getUserOrDefault import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.util.toMatrixItem import org.matrix.android.sdk.api.util.toMatrixItem
import timber.log.Timber import timber.log.Timber
@ -89,8 +90,9 @@ class IncomingVerificationRequestHandler @Inject constructor(
// TODO maybe check also if // TODO maybe check also if
val uid = "kvr_${tx.transactionId}" val uid = "kvr_${tx.transactionId}"
// TODO we don't have that anymore? as it has to be requested first? // TODO we don't have that anymore? as it has to be requested first?
when (tx.state) { if (tx !is SasVerificationTransaction) return
is VerificationTxState.SasStarted -> { when (tx.state()) {
is SasTransactionState.SasStarted -> {
// Add a notification for every incoming request // Add a notification for every incoming request
// val user = session.getUserOrDefault(tx.otherUserId).toMatrixItem() // val user = session.getUserOrDefault(tx.otherUserId).toMatrixItem()
// val name = user.getBestName() // val name = user.getBestName()
@ -139,7 +141,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
// } // }
// popupAlertManager.postVectorAlert(alert) // popupAlertManager.postVectorAlert(alert)
} }
is VerificationTxState.TerminalTxState -> { is SasTransactionState.Done -> {
// cancel related notification // cancel related notification
popupAlertManager.cancelAlert(uid) popupAlertManager.cancelAlert(uid)
} }
@ -186,7 +188,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
val roomId = pr.roomId val roomId = pr.roomId
if (roomId.isNullOrBlank()) { if (roomId.isNullOrBlank()) {
// TODO // TODO
//it.navigator.waitSessionVerification(it) // it.navigator.waitSessionVerification(it)
} else { } else {
it.navigator.openRoom( it.navigator.openRoom(
context = it, context = it,
@ -213,9 +215,9 @@ class IncomingVerificationRequestHandler @Inject constructor(
override fun verificationRequestUpdated(pr: PendingVerificationRequest) { override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
// If an incoming request is readied (by another device?) we should discard the alert // If an incoming request is readied (by another device?) we should discard the alert
if (pr.isIncoming && (pr.state == EVerificationState.HandledByOtherSession if (pr.isIncoming && (pr.state == EVerificationState.HandledByOtherSession ||
|| pr.state == EVerificationState.Started pr.state == EVerificationState.Started ||
|| pr.state == EVerificationState.WeStarted)) { pr.state == EVerificationState.WeStarted)) {
popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr)) popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr))
} }
} }

View File

@ -1,266 +1,228 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification // package im.vector.app.features.crypto.verification
//
import android.app.Activity // import android.app.Activity
import android.app.Dialog // import android.app.Dialog
import android.os.Bundle // import android.os.Bundle
import android.os.Parcelable // import android.os.Parcelable
import android.view.KeyEvent // import android.view.KeyEvent
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import androidx.fragment.app.Fragment // import androidx.fragment.app.Fragment
import com.airbnb.mvrx.fragmentViewModel // import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder // import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.extensions.commitTransaction // import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.registerStartForActivityResult // import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.toMvRxBundle // import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.platform.VectorBaseActivity // import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment // import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetVerificationBinding // import im.vector.app.databinding.BottomSheetVerificationBinding
import im.vector.app.features.crypto.quads.SharedSecureStorageActivity // import im.vector.app.features.crypto.quads.SharedSecureStorageActivity
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment // import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment // import im.vector.app.features.crypto.verification.cancel.VerificationNotMeFragment
import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment // import im.vector.app.features.crypto.verification.choose.VerificationChooseMethodFragment
import im.vector.app.features.crypto.verification.conclusion.VerificationConclusionFragment // import im.vector.app.features.crypto.verification.conclusion.VerificationConclusionFragment
import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeFragment // import im.vector.app.features.crypto.verification.emoji.VerificationEmojiCodeFragment
import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment // import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment
import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment // import im.vector.app.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
import im.vector.app.features.crypto.verification.request.VerificationRequestFragment // import im.vector.app.features.crypto.verification.request.VerificationRequestFragment
import im.vector.app.features.displayname.getBestName // import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer // import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.settings.VectorSettingsActivity // import im.vector.app.features.settings.VectorSettingsActivity
import kotlinx.parcelize.Parcelize // import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME // import org.matrix.android.sdk.api.session.crypto.crosssigning.KEYBACKUP_SECRET_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME // import org.matrix.android.sdk.api.session.crypto.crosssigning.MASTER_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME // import org.matrix.android.sdk.api.session.crypto.crosssigning.SELF_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME // import org.matrix.android.sdk.api.session.crypto.crosssigning.USER_SIGNING_KEY_SSSS_NAME
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel // import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode // import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState // import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState // import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import timber.log.Timber // import timber.log.Timber
import javax.inject.Inject // import javax.inject.Inject
import kotlin.reflect.KClass // import kotlin.reflect.KClass
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetVerificationBinding>() { // class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetVerificationBinding>() {
//
@Parcelize // @Parcelize
data class VerificationArgs( // data class VerificationArgs(
val otherUserId: String, // val otherUserId: String,
// might be null for self verification if there is no device to request to // // might be null for self verification if there is no device to request to
// in this case you could use 4S (or reset all) // // in this case you could use 4S (or reset all)
val verificationId: String?, // val verificationId: String?,
// val verificationLocalId: String? = null, // // val verificationLocalId: String? = null,
val roomId: String? = null, // val roomId: String? = null,
// Special mode where UX should show loading wheel until other session sends a request/tx // // Special mode where UX should show loading wheel until other session sends a request/tx
val selfVerificationMode: Boolean = false // val selfVerificationMode: Boolean = false
) : Parcelable // ) : Parcelable
//
override val showExpanded = true // override val showExpanded = true
//
@Inject // @Inject
lateinit var avatarRenderer: AvatarRenderer // lateinit var avatarRenderer: AvatarRenderer
//
private val viewModel by fragmentViewModel(VerificationBottomSheetViewModel::class) // private val viewModel by fragmentViewModel(VerificationBottomSheetViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationBinding {
return BottomSheetVerificationBinding.inflate(inflater, container, false) // return BottomSheetVerificationBinding.inflate(inflater, container, false)
} // }
//
init { // init {
isCancelable = false // isCancelable = false
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
//
viewModel.observeViewEvents { // viewModel.observeViewEvents {
when (it) { // when (it) {
is VerificationBottomSheetViewEvents.Dismiss -> dismiss() // is VerificationBottomSheetViewEvents.Dismiss -> dismiss()
is VerificationBottomSheetViewEvents.AccessSecretStore -> { // is VerificationBottomSheetViewEvents.AccessSecretStore -> {
secretStartForActivityResult.launch( // secretStartForActivityResult.launch(
SharedSecureStorageActivity.newReadIntent( // SharedSecureStorageActivity.newReadIntent(
requireContext(), // requireContext(),
null, // use default key // null, // use default key
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME), // listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME),
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS // SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
) // )
) // )
} // }
is VerificationBottomSheetViewEvents.ModalError -> { // is VerificationBottomSheetViewEvents.ModalError -> {
MaterialAlertDialogBuilder(requireContext()) // MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.dialog_title_error)) // .setTitle(getString(R.string.dialog_title_error))
.setMessage(it.errorMessage) // .setMessage(it.errorMessage)
.setCancelable(false) // .setCancelable(false)
.setPositiveButton(R.string.ok, null) // .setPositiveButton(R.string.ok, null)
.show() // .show()
Unit // Unit
} // }
VerificationBottomSheetViewEvents.GoToSettings -> { // VerificationBottomSheetViewEvents.GoToSettings -> {
dismiss() // dismiss()
(activity as? VectorBaseActivity<*>)?.let { activity -> // (activity as? VectorBaseActivity<*>)?.let { activity ->
activity.navigator.openSettings(activity, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY) // activity.navigator.openSettings(activity, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY)
} // }
} // }
} // }
} // }
} // }
//
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { // override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).apply { // return super.onCreateDialog(savedInstanceState).apply {
setOnKeyListener { _, keyCode, keyEvent -> // setOnKeyListener { _, keyCode, keyEvent ->
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) { // if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
viewModel.queryCancel() // viewModel.queryCancel()
true // true
} else { // } else {
false // false
} // }
} // }
} // }
} // }
//
private val secretStartForActivityResult = registerStartForActivityResult { activityResult -> // private val secretStartForActivityResult = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) { // if (activityResult.resultCode == Activity.RESULT_OK) {
val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT) // val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
val reset = activityResult.data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false // val reset = activityResult.data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false
if (result != null) { // if (result != null) {
viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS)) // viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS))
} else if (reset) { // } else if (reset) {
// all have been reset, so we are verified? // // all have been reset, so we are verified?
viewModel.handle(VerificationAction.SecuredStorageHasBeenReset) // viewModel.handle(VerificationAction.SecuredStorageHasBeenReset)
} // }
} else { // } else {
viewModel.handle(VerificationAction.CancelledFromSsss) // viewModel.handle(VerificationAction.CancelledFromSsss)
} // }
} // }
//
override fun invalidate() = withState(viewModel) { state -> // override fun invalidate() = withState(viewModel) { state ->
avatarRenderer.render(state.otherUserMxItem, views.otherUserAvatarImageView) // avatarRenderer.render(state.otherUserMxItem, views.otherUserAvatarImageView)
if (state.isMe) { // if (state.isMe) {
if (state.sasTransactionState == VerificationTxState.Verified || // if (state.sasTransactionState == VerificationTxState.Verified ||
state.qrTransactionState == VerificationTxState.Verified || // state.qrTransactionState == VerificationTxState.Verified ||
state.verifiedFromPrivateKeys) { // state.verifiedFromPrivateKeys) {
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted) // views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
} else { // } else {
views.otherUserShield.render(RoomEncryptionTrustLevel.Warning) // views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
} // }
views.otherUserNameText.text = getString( // views.otherUserNameText.text = getString(
if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session // if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session
) // )
} else { // } else {
if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) { // if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
views.otherUserNameText.text = getString(R.string.verification_verified_user, state.otherUserMxItem.getBestName()) // views.otherUserNameText.text = getString(R.string.verification_verified_user, state.otherUserMxItem.getBestName())
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted) // views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
} else { // } else {
views.otherUserNameText.text = getString(R.string.verification_verify_user, state.otherUserMxItem.getBestName()) // views.otherUserNameText.text = getString(R.string.verification_verify_user, state.otherUserMxItem.getBestName())
views.otherUserShield.render(null) // views.otherUserShield.render(null)
} // }
} // }
//
if (state.quadSHasBeenReset) { // if (state.quadSHasBeenReset) {
showFragment( // showFragment(
VerificationConclusionFragment::class, // VerificationConclusionFragment::class,
VerificationConclusionFragment.Args( // VerificationConclusionFragment.Args(
isSuccessFull = true, // isSuccessFull = true,
isMe = true, // isMe = true,
cancelReason = null // cancelReason = null
) // )
) // )
return@withState // return@withState
} // }
//
if (state.userThinkItsNotHim) { // if (state.userThinkItsNotHim) {
views.otherUserNameText.text = getString(R.string.dialog_title_warning) // views.otherUserNameText.text = getString(R.string.dialog_title_warning)
showFragment(VerificationNotMeFragment::class) // showFragment(VerificationNotMeFragment::class)
return@withState // return@withState
} // }
//
if (state.userWantsToCancel) { // if (state.userWantsToCancel) {
views.otherUserNameText.text = getString(R.string.are_you_sure) // views.otherUserNameText.text = getString(R.string.are_you_sure)
showFragment(VerificationCancelFragment::class) // showFragment(VerificationCancelFragment::class)
return@withState // return@withState
} // }
//
if (state.selfVerificationMode && state.verifyingFrom4S) { // if (state.selfVerificationMode && state.verifyingFrom4S) {
showFragment(QuadSLoadingFragment::class) // showFragment(QuadSLoadingFragment::class)
return@withState // return@withState
} // }
if (state.selfVerificationMode && state.verifiedFromPrivateKeys) { // if (state.selfVerificationMode && state.verifiedFromPrivateKeys) {
showFragment( // showFragment(
VerificationConclusionFragment::class, // VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(true, null, state.isMe) // VerificationConclusionFragment.Args(true, null, state.isMe)
) // )
return@withState // return@withState
} // }
//
// Did the request result in a SAS transaction? // // Did the request result in a SAS transaction?
if (state.sasTransactionState != null) { // if (state.sasTransactionState != null) {
when (state.sasTransactionState) { // when (state.sasTransactionState) {
//
VerificationTxState.None, // VerificationTxState.None,
VerificationTxState.SasStarted, // VerificationTxState.SasStarted,
VerificationTxState.SasKeySent, // VerificationTxState.SasKeySent,
VerificationTxState.SasShortCodeReady, // VerificationTxState.SasShortCodeReady,
VerificationTxState.SasMacSent, // VerificationTxState.SasMacSent,
is VerificationTxState.SasMacReceived, // is VerificationTxState.SasMacReceived,
VerificationTxState.SasAccepted -> { // VerificationTxState.SasAccepted -> {
showFragment(
VerificationEmojiCodeFragment::class,
VerificationArgs(
state.otherUserId,
// If it was outgoing it.transaction id would be null, but the pending request
// would be updated (from localId to txId)
state.pendingRequest.invoke()?.transactionId ?: state.transactionId
)
)
}
is VerificationTxState.Verified -> {
showFragment(
VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(true, null, state.isMe)
)
}
is VerificationTxState.Cancelled -> {
showFragment(
VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(false, state.sasTransactionState.cancelCode.value, state.isMe)
)
}
else -> Unit
// is VerificationTxState.None,
// is VerificationTxState.SendingStart,
// is VerificationTxState.Started,
// is VerificationTxState.OnStarted,
// is VerificationTxState.SendingAccept,
// is VerificationTxState.Accepted,
// is VerificationTxState.OnAccepted,
// is VerificationTxState.SendingKey,
// is VerificationTxState.KeySent,
// is VerificationTxState.OnKeyReceived,
// is VerificationTxState.ShortCodeReady,
// is VerificationTxState.ShortCodeAccepted,
// is VerificationTxState.SendingMac,
// is VerificationTxState.MacSent,
// is VerificationTxState.Verifying -> {
// showFragment( // showFragment(
// VerificationEmojiCodeFragment::class, // VerificationEmojiCodeFragment::class,
// VerificationArgs( // VerificationArgs(
@ -284,156 +246,194 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
// ) // )
// } // }
// else -> Unit // else -> Unit
} // // is VerificationTxState.None,
// // is VerificationTxState.SendingStart,
return@withState // // is VerificationTxState.Started,
} // // is VerificationTxState.OnStarted,
// // is VerificationTxState.SendingAccept,
when (state.qrTransactionState) { // // is VerificationTxState.Accepted,
is VerificationTxState.QrScannedByOther -> { // // is VerificationTxState.OnAccepted,
showFragment(VerificationQrScannedByOtherFragment::class) // // is VerificationTxState.SendingKey,
return@withState // // is VerificationTxState.KeySent,
} // // is VerificationTxState.OnKeyReceived,
//TODO // // is VerificationTxState.ShortCodeReady,
// is VerificationTxState.Started, // // is VerificationTxState.ShortCodeAccepted,
is VerificationTxState.WaitingOtherReciprocateConfirm -> { // // is VerificationTxState.SendingMac,
showFragment( // // is VerificationTxState.MacSent,
VerificationQRWaitingFragment::class, // // is VerificationTxState.Verifying -> {
VerificationQRWaitingFragment.Args( // // showFragment(
isMe = state.isMe, // // VerificationEmojiCodeFragment::class,
otherUserName = state.otherUserMxItem.getBestName() // // VerificationArgs(
) // // state.otherUserId,
) // // // If it was outgoing it.transaction id would be null, but the pending request
return@withState // // // would be updated (from localId to txId)
} // // state.pendingRequest.invoke()?.transactionId ?: state.transactionId
is VerificationTxState.Verified -> { // // )
showFragment( // // )
VerificationConclusionFragment::class, // // }
VerificationConclusionFragment.Args(true, null, state.isMe) // // is VerificationTxState.Verified -> {
) // // showFragment(
return@withState // // VerificationConclusionFragment::class,
} // // VerificationConclusionFragment.Args(true, null, state.isMe)
is VerificationTxState.Cancelled -> { // // )
showFragment( // // }
VerificationConclusionFragment::class, // // is VerificationTxState.Cancelled -> {
VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe) // // showFragment(
) // // VerificationConclusionFragment::class,
return@withState // // VerificationConclusionFragment.Args(false, state.sasTransactionState.cancelCode.value, state.isMe)
} // // )
else -> Unit // // }
} // // else -> Unit
// }
// At this point there is no SAS transaction for this request //
// return@withState
// Transaction has not yet started // }
if (state.pendingRequest.invoke()?.cancelConclusion != null) { //
// The request has been declined, we should dismiss // when (state.qrTransactionState) {
views.otherUserNameText.text = getString(R.string.verification_cancelled) // is VerificationTxState.QrScannedByOther -> {
showFragment( // showFragment(VerificationQrScannedByOtherFragment::class)
VerificationConclusionFragment::class, // return@withState
VerificationConclusionFragment.Args( // }
isSuccessFull = false, // //TODO
cancelReason = state.pendingRequest.invoke()?.cancelConclusion?.value ?: CancelCode.User.value, // // is VerificationTxState.Started,
isMe = state.isMe // is VerificationTxState.WaitingOtherReciprocateConfirm -> {
) // showFragment(
) // VerificationQRWaitingFragment::class,
return@withState // VerificationQRWaitingFragment.Args(
} // isMe = state.isMe,
// otherUserName = state.otherUserMxItem.getBestName()
// If it's an outgoing // )
if (state.pendingRequest.invoke() == null || state.pendingRequest.invoke()?.isIncoming == false || state.selfVerificationMode) { // )
Timber.v("## SAS show bottom sheet for outgoing request") // return@withState
if (state.pendingRequest.invoke()?.state == EVerificationState.Ready) { // }
Timber.v("## SAS show bottom sheet for outgoing and ready request") // is VerificationTxState.Verified -> {
// Show choose method fragment with waiting // showFragment(
showFragment( // VerificationConclusionFragment::class,
VerificationChooseMethodFragment::class, // VerificationConclusionFragment.Args(true, null, state.isMe)
VerificationArgs( // )
otherUserId = state.otherUserId, // return@withState
verificationId = state.pendingRequest.invoke()?.transactionId // }
) // is VerificationTxState.Cancelled -> {
) // showFragment(
} else { // VerificationConclusionFragment::class,
// Stay on the start fragment // VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe)
showFragment( // )
VerificationRequestFragment::class, // return@withState
VerificationArgs( // }
otherUserId = state.otherUserId, // else -> Unit
verificationId = state.pendingRequest.invoke()?.transactionId, // }
) //
) // // At this point there is no SAS transaction for this request
} //
} else if (state.pendingRequest.invoke()?.isIncoming == true) { // // Transaction has not yet started
Timber.v("## SAS show bottom sheet for Incoming request") // if (state.pendingRequest.invoke()?.cancelConclusion != null) {
// For incoming we can switch to choose method because ready is being sent or already sent // // The request has been declined, we should dismiss
showFragment( // views.otherUserNameText.text = getString(R.string.verification_cancelled)
VerificationChooseMethodFragment::class, // showFragment(
VerificationArgs( // VerificationConclusionFragment::class,
otherUserId = state.otherUserId, // VerificationConclusionFragment.Args(
verificationId = state.pendingRequest.invoke()?.transactionId // isSuccessFull = false,
) // cancelReason = state.pendingRequest.invoke()?.cancelConclusion?.value ?: CancelCode.User.value,
) // isMe = state.isMe
} // )
super.invalidate() // )
} // return@withState
// }
private fun showFragment(fragmentClass: KClass<out Fragment>, argsParcelable: Parcelable? = null) { //
if (childFragmentManager.findFragmentByTag(fragmentClass.simpleName) == null) { // // If it's an outgoing
childFragmentManager.commitTransaction { // if (state.pendingRequest.invoke() == null || state.pendingRequest.invoke()?.isIncoming == false || state.selfVerificationMode) {
replace( // Timber.v("## SAS show bottom sheet for outgoing request")
R.id.bottomSheetFragmentContainer, // if (state.pendingRequest.invoke()?.state == EVerificationState.Ready) {
fragmentClass.java, // Timber.v("## SAS show bottom sheet for outgoing and ready request")
argsParcelable?.toMvRxBundle(), // // Show choose method fragment with waiting
fragmentClass.simpleName // showFragment(
) // VerificationChooseMethodFragment::class,
} // VerificationArgs(
} // otherUserId = state.otherUserId,
} // verificationId = state.pendingRequest.invoke()?.transactionId
// )
companion object { // )
fun withArgs(otherUserId: String, transactionId: String): VerificationBottomSheet { // } else {
return VerificationBottomSheet().apply { // // Stay on the start fragment
setArguments( // showFragment(
VerificationArgs( // VerificationRequestFragment::class,
otherUserId = otherUserId, // VerificationArgs(
verificationId = transactionId, // otherUserId = state.otherUserId,
) // verificationId = state.pendingRequest.invoke()?.transactionId,
) // )
} // )
} // }
// } else if (state.pendingRequest.invoke()?.isIncoming == true) {
// fun forSelfVerification(session: Session): VerificationBottomSheet { // Timber.v("## SAS show bottom sheet for Incoming request")
// // For incoming we can switch to choose method because ready is being sent or already sent
// showFragment(
// VerificationChooseMethodFragment::class,
// VerificationArgs(
// otherUserId = state.otherUserId,
// verificationId = state.pendingRequest.invoke()?.transactionId
// )
// )
// }
// super.invalidate()
// }
//
// private fun showFragment(fragmentClass: KClass<out Fragment>, argsParcelable: Parcelable? = null) {
// if (childFragmentManager.findFragmentByTag(fragmentClass.simpleName) == null) {
// childFragmentManager.commitTransaction {
// replace(
// R.id.bottomSheetFragmentContainer,
// fragmentClass.java,
// argsParcelable?.toMvRxBundle(),
// fragmentClass.simpleName
// )
// }
// }
// }
//
// companion object {
// fun withArgs(otherUserId: String, transactionId: String): VerificationBottomSheet {
// return VerificationBottomSheet().apply { // return VerificationBottomSheet().apply {
// setArguments( // setArguments(
// VerificationArgs( // VerificationArgs(
// otherUserId = session.myUserId, // otherUserId = otherUserId,
// selfVerificationMode = true // verificationId = transactionId,
// ) // )
// ) // )
// } // }
// } // }
//
// fun forSelfVerification(session: Session, outgoingRequest: String): VerificationBottomSheet { // // fun forSelfVerification(session: Session): VerificationBottomSheet {
// return VerificationBottomSheet().apply { // // return VerificationBottomSheet().apply {
// setArguments( // // setArguments(
// VerificationArgs( // // VerificationArgs(
// otherUserId = session.myUserId, // // otherUserId = session.myUserId,
// selfVerificationMode = true, // // selfVerificationMode = true
// verificationId = outgoingRequest // // )
// ) // // )
// ) // // }
// // }
//
// // fun forSelfVerification(session: Session, outgoingRequest: String): VerificationBottomSheet {
// // return VerificationBottomSheet().apply {
// // setArguments(
// // VerificationArgs(
// // otherUserId = session.myUserId,
// // selfVerificationMode = true,
// // verificationId = outgoingRequest
// // )
// // )
// // }
// // }
//
// // const val WAITING_SELF_VERIF_TAG: String = "WAITING_SELF_VERIF_TAG"
// } // }
// } // }
//
// const val WAITING_SELF_VERIF_TAG: String = "WAITING_SELF_VERIF_TAG" // // fun View.getParentCoordinatorLayout(): CoordinatorLayout? {
} // // var current = this as? View
} // // while (current != null) {
// // if (current is CoordinatorLayout) return current
// fun View.getParentCoordinatorLayout(): CoordinatorLayout? { // // current = current.parent as? View
// var current = this as? View // // }
// while (current != null) { // // return null
// if (current is CoordinatorLayout) return current // // }
// current = current.parent as? View
// }
// return null
// }

View File

@ -1,109 +1,109 @@
/* // /*
* Copyright (c) 2020 New Vector Ltd // * Copyright (c) 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.cancel // package im.vector.app.features.crypto.verification.cancel
//
import androidx.core.text.toSpannable // import androidx.core.text.toSpannable
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.colorizeMatchingText // import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.displayname.getBestName // import im.vector.app.features.displayname.getBestName
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationCancelController @Inject constructor( // class VerificationCancelController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider // private val colorProvider: ColorProvider
) : EpoxyController() { // ) : EpoxyController() {
//
var listener: Listener? = null // var listener: Listener? = null
//
private var viewState: VerificationBottomSheetViewState? = null // private var viewState: VerificationBottomSheetViewState? = null
//
fun update(viewState: VerificationBottomSheetViewState) { // fun update(viewState: VerificationBottomSheetViewState) {
this.viewState = viewState // this.viewState = viewState
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val state = viewState ?: return // val state = viewState ?: return
val host = this // val host = this
if (state.isMe) { // if (state.isMe) {
if (state.currentDeviceCanCrossSign) { // if (state.currentDeviceCanCrossSign) {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_trusted).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_trusted).toEpoxyCharSequence())
} // }
} else { // } else {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted).toEpoxyCharSequence())
} // }
} // }
} else { // } else {
val otherUserID = state.otherUserId // val otherUserID = state.otherUserId
val otherDisplayName = state.otherUserMxItem.getBestName() // val otherDisplayName = state.otherUserMxItem.getBestName()
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice( // notice(
EpoxyCharSequence( // EpoxyCharSequence(
host.stringProvider.getString(R.string.verify_cancel_other, otherDisplayName, otherUserID) // host.stringProvider.getString(R.string.verify_cancel_other, otherDisplayName, otherUserID)
.toSpannable() // .toSpannable()
.colorizeMatchingText(otherUserID, host.colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color)) // .colorizeMatchingText(otherUserID, host.colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
) // )
) // )
} // }
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("cancel") // id("cancel")
title(host.stringProvider.getString(R.string.action_skip)) // title(host.stringProvider.getString(R.string.action_skip))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
listener { host.listener?.onTapCancel() } // listener { host.listener?.onTapCancel() }
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep1") // id("sep1")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("continue") // id("continue")
title(host.stringProvider.getString(R.string._continue)) // title(host.stringProvider.getString(R.string._continue))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener { host.listener?.onTapContinue() } // listener { host.listener?.onTapContinue() }
} // }
} // }
//
interface Listener { // interface Listener {
fun onTapCancel() // fun onTapCancel()
fun onTapContinue() // fun onTapContinue()
} // }
} // }

View File

@ -1,73 +1,73 @@
/* // /*
* Copyright (c) 2020 New Vector Ltd // * Copyright (c) 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.cancel // package im.vector.app.features.crypto.verification.cancel
//
import android.os.Bundle // import android.os.Bundle
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.cleanup // import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith // import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import javax.inject.Inject // import javax.inject.Inject
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationCancelFragment : // class VerificationCancelFragment :
VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(), // VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationCancelController.Listener { // VerificationCancelController.Listener {
//
@Inject lateinit var controller: VerificationCancelController // @Inject lateinit var controller: VerificationCancelController
//
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) // private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
setupRecyclerView() // setupRecyclerView()
} // }
//
override fun onDestroyView() { // override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup() // views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null // controller.listener = null
super.onDestroyView() // super.onDestroyView()
} // }
//
private fun setupRecyclerView() { // private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) // views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this // controller.listener = this
} // }
//
override fun invalidate() = withState(viewModel) { state -> // override fun invalidate() = withState(viewModel) { state ->
controller.update(state) // controller.update(state)
} // }
//
override fun onTapCancel() { // override fun onTapCancel() {
viewModel.confirmCancel() // viewModel.confirmCancel()
} // }
//
override fun onTapContinue() { // override fun onTapContinue() {
viewModel.continueFromCancel() // viewModel.continueFromCancel()
} // }
} // }

View File

@ -1,84 +1,84 @@
/* // /*
* Copyright (c) 2020 New Vector Ltd // * Copyright (c) 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.cancel // package im.vector.app.features.crypto.verification.cancel
//
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.html.EventHtmlRenderer // import im.vector.app.features.html.EventHtmlRenderer
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationNotMeController @Inject constructor( // class VerificationNotMeController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider, // private val colorProvider: ColorProvider,
private val eventHtmlRenderer: EventHtmlRenderer // private val eventHtmlRenderer: EventHtmlRenderer
) : EpoxyController() { // ) : EpoxyController() {
//
var listener: Listener? = null // var listener: Listener? = null
//
private var viewState: VerificationBottomSheetViewState? = null // private var viewState: VerificationBottomSheetViewState? = null
//
fun update(viewState: VerificationBottomSheetViewState) { // fun update(viewState: VerificationBottomSheetViewState) {
this.viewState = viewState // this.viewState = viewState
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val host = this // val host = this
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verify_not_me_self_verification)).toEpoxyCharSequence()) // notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verify_not_me_self_verification)).toEpoxyCharSequence())
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("skip") // id("skip")
title(host.stringProvider.getString(R.string.action_skip)) // title(host.stringProvider.getString(R.string.action_skip))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onTapSkip() } // listener { host.listener?.onTapSkip() }
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep1") // id("sep1")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("settings") // id("settings")
title(host.stringProvider.getString(R.string.settings)) // title(host.stringProvider.getString(R.string.settings))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener { host.listener?.onTapSettings() } // listener { host.listener?.onTapSettings() }
} // }
} // }
//
interface Listener { // interface Listener {
fun onTapSkip() // fun onTapSkip()
fun onTapSettings() // fun onTapSettings()
} // }
} // }

View File

@ -1,73 +1,73 @@
/* // /*
* Copyright (c) 2020 New Vector Ltd // * Copyright (c) 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.cancel // package im.vector.app.features.crypto.verification.cancel
//
import android.os.Bundle // import android.os.Bundle
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.cleanup // import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith // import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import javax.inject.Inject // import javax.inject.Inject
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationNotMeFragment : // class VerificationNotMeFragment :
VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(), // VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationNotMeController.Listener { // VerificationNotMeController.Listener {
//
@Inject lateinit var controller: VerificationNotMeController // @Inject lateinit var controller: VerificationNotMeController
//
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) // private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
setupRecyclerView() // setupRecyclerView()
} // }
//
override fun onDestroyView() { // override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup() // views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null // controller.listener = null
super.onDestroyView() // super.onDestroyView()
} // }
//
private fun setupRecyclerView() { // private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) // views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this // controller.listener = this
} // }
//
override fun invalidate() = withState(viewModel) { state -> // override fun invalidate() = withState(viewModel) { state ->
controller.update(state) // controller.update(state)
} // }
//
override fun onTapSkip() { // override fun onTapSkip() {
viewModel.continueFromWasNotMe() // viewModel.continueFromWasNotMe()
} // }
//
override fun onTapSettings() { // override fun onTapSettings() {
viewModel.goToSettings() // viewModel.goToSettings()
} // }
} // }

View File

@ -1,147 +1,147 @@
/* // /*
* Copyright 2020 New Vector Ltd // * Copyright 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.choose // package im.vector.app.features.crypto.verification.choose
//
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.core.ui.list.buttonPositiveDestructiveButtonBarItem // import im.vector.app.core.ui.list.buttonPositiveDestructiveButtonBarItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationQrCodeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationQrCodeItem
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationChooseMethodController @Inject constructor( // class VerificationChooseMethodController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider // private val colorProvider: ColorProvider
) : EpoxyController() { // ) : EpoxyController() {
//
var listener: Listener? = null // var listener: Listener? = null
//
private var viewState: VerificationChooseMethodViewState? = null // private var viewState: VerificationChooseMethodViewState? = null
//
fun update(viewState: VerificationChooseMethodViewState) { // fun update(viewState: VerificationChooseMethodViewState) {
this.viewState = viewState // this.viewState = viewState
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val state = viewState ?: return // val state = viewState ?: return
val host = this // val host = this
//
if (state.otherCanScanQrCode || state.otherCanShowQrCode) { // if (state.otherCanScanQrCode || state.otherCanShowQrCode) {
val scanCodeInstructions: String // val scanCodeInstructions: String
val scanOtherCodeTitle: String // val scanOtherCodeTitle: String
val compareEmojiSubtitle: String // val compareEmojiSubtitle: String
if (state.isMe) { // if (state.isMe) {
scanCodeInstructions = stringProvider.getString(R.string.verification_scan_self_notice) // scanCodeInstructions = stringProvider.getString(R.string.verification_scan_self_notice)
scanOtherCodeTitle = stringProvider.getString(R.string.verification_scan_with_this_device) // scanOtherCodeTitle = stringProvider.getString(R.string.verification_scan_with_this_device)
compareEmojiSubtitle = stringProvider.getString(R.string.verification_scan_self_emoji_subtitle) // compareEmojiSubtitle = stringProvider.getString(R.string.verification_scan_self_emoji_subtitle)
} else { // } else {
scanCodeInstructions = stringProvider.getString(R.string.verification_scan_notice) // scanCodeInstructions = stringProvider.getString(R.string.verification_scan_notice)
scanOtherCodeTitle = stringProvider.getString(R.string.verification_scan_their_code) // scanOtherCodeTitle = stringProvider.getString(R.string.verification_scan_their_code)
compareEmojiSubtitle = stringProvider.getString(R.string.verification_scan_emoji_subtitle) // compareEmojiSubtitle = stringProvider.getString(R.string.verification_scan_emoji_subtitle)
} // }
//
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(scanCodeInstructions.toEpoxyCharSequence()) // notice(scanCodeInstructions.toEpoxyCharSequence())
} // }
//
if (state.otherCanScanQrCode && !state.qrCodeText.isNullOrBlank()) { // if (state.otherCanScanQrCode && !state.qrCodeText.isNullOrBlank()) {
bottomSheetVerificationQrCodeItem { // bottomSheetVerificationQrCodeItem {
id("qr") // id("qr")
data(state.qrCodeText) // data(state.qrCodeText)
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
} // }
//
if (state.otherCanShowQrCode) { // if (state.otherCanShowQrCode) {
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("openCamera") // id("openCamera")
title(scanOtherCodeTitle) // title(scanOtherCodeTitle)
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_camera) // iconRes(R.drawable.ic_camera)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener { host.listener?.openCamera() } // listener { host.listener?.openCamera() }
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep1") // id("sep1")
} // }
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("openEmoji") // id("openEmoji")
title(host.stringProvider.getString(R.string.verification_scan_emoji_title)) // title(host.stringProvider.getString(R.string.verification_scan_emoji_title))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
subTitle(compareEmojiSubtitle) // subTitle(compareEmojiSubtitle)
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.doVerifyBySas() } // listener { host.listener?.doVerifyBySas() }
} // }
} else if (state.sasModeAvailable) { // } else if (state.sasModeAvailable) {
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("openEmoji") // id("openEmoji")
title(host.stringProvider.getString(R.string.verification_no_scan_emoji_title)) // title(host.stringProvider.getString(R.string.verification_no_scan_emoji_title))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.doVerifyBySas() } // listener { host.listener?.doVerifyBySas() }
} // }
} else if (!state.isReadySent) { // } else if (!state.isReadySent) {
// a bit of a special case, if you tapped on the timeline cell but not on a button // // a bit of a special case, if you tapped on the timeline cell but not on a button
buttonPositiveDestructiveButtonBarItem { // buttonPositiveDestructiveButtonBarItem {
id("accept_decline") // id("accept_decline")
positiveText(host.stringProvider.getString(R.string.action_accept).toEpoxyCharSequence()) // positiveText(host.stringProvider.getString(R.string.action_accept).toEpoxyCharSequence())
destructiveText(host.stringProvider.getString(R.string.action_decline).toEpoxyCharSequence()) // destructiveText(host.stringProvider.getString(R.string.action_decline).toEpoxyCharSequence())
positiveButtonClickAction { host.listener?.acceptRequest() } // positiveButtonClickAction { host.listener?.acceptRequest() }
destructiveButtonClickAction { host.listener?.declineRequest() } // destructiveButtonClickAction { host.listener?.declineRequest() }
} // }
} // }
//
if (state.isMe && state.canCrossSign) { // if (state.isMe && state.canCrossSign) {
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep_notMe") // id("sep_notMe")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("wasnote") // id("wasnote")
title(host.stringProvider.getString(R.string.verify_new_session_was_not_me)) // title(host.stringProvider.getString(R.string.verify_new_session_was_not_me))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
subTitle(host.stringProvider.getString(R.string.verify_new_session_compromized)) // subTitle(host.stringProvider.getString(R.string.verify_new_session_compromized))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onClickOnWasNotMe() } // listener { host.listener?.onClickOnWasNotMe() }
} // }
} // }
} // }
//
interface Listener { // interface Listener {
fun openCamera() // fun openCamera()
fun doVerifyBySas() // fun doVerifyBySas()
fun onClickOnWasNotMe() // fun onClickOnWasNotMe()
fun acceptRequest() // fun acceptRequest()
fun declineRequest() // fun declineRequest()
} // }
} // }

View File

@ -1,136 +1,136 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.choose // package im.vector.app.features.crypto.verification.choose
//
import android.app.Activity // import android.app.Activity
import android.os.Bundle // import android.os.Bundle
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel // import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.extensions.cleanup // import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith // import im.vector.app.core.extensions.configureWith
import im.vector.app.core.extensions.registerStartForActivityResult // import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO // import im.vector.app.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
import im.vector.app.core.utils.checkPermissions // import im.vector.app.core.utils.checkPermissions
import im.vector.app.core.utils.onPermissionDeniedDialog // import im.vector.app.core.utils.onPermissionDeniedDialog
import im.vector.app.core.utils.registerForPermissionsResult // import im.vector.app.core.utils.registerForPermissionsResult
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction // import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import im.vector.app.features.qrcode.QrCodeScannerActivity // import im.vector.app.features.qrcode.QrCodeScannerActivity
import timber.log.Timber // import timber.log.Timber
import javax.inject.Inject // import javax.inject.Inject
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationChooseMethodFragment : // class VerificationChooseMethodFragment :
VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(), // VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationChooseMethodController.Listener { // VerificationChooseMethodController.Listener {
//
@Inject lateinit var controller: VerificationChooseMethodController // @Inject lateinit var controller: VerificationChooseMethodController
private val viewModel by fragmentViewModel(VerificationChooseMethodViewModel::class) // private val viewModel by fragmentViewModel(VerificationChooseMethodViewModel::class)
//
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) // private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
//
setupRecyclerView() // setupRecyclerView()
} // }
//
override fun onDestroyView() { // override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup() // views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null // controller.listener = null
super.onDestroyView() // super.onDestroyView()
} // }
//
private fun setupRecyclerView() { // private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) // views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this // controller.listener = this
} // }
//
override fun invalidate() = withState(viewModel) { state -> // override fun invalidate() = withState(viewModel) { state ->
controller.update(state) // controller.update(state)
} // }
//
override fun doVerifyBySas() = withState(sharedViewModel) { state -> // override fun doVerifyBySas() = withState(sharedViewModel) { state ->
sharedViewModel.handle( // sharedViewModel.handle(
VerificationAction.StartSASVerification // VerificationAction.StartSASVerification
) // )
} // }
//
private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted, deniedPermanently -> // private val openCameraActivityResultLauncher = registerForPermissionsResult { allGranted, deniedPermanently ->
if (allGranted) { // if (allGranted) {
doOpenQRCodeScanner() // doOpenQRCodeScanner()
} else if (deniedPermanently) { // } else if (deniedPermanently) {
activity?.onPermissionDeniedDialog(R.string.denied_permission_camera) // activity?.onPermissionDeniedDialog(R.string.denied_permission_camera)
} // }
} // }
//
override fun openCamera() { // override fun openCamera() {
if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) { // if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, requireActivity(), openCameraActivityResultLauncher)) {
doOpenQRCodeScanner() // doOpenQRCodeScanner()
} // }
} // }
//
override fun onClickOnWasNotMe() { // override fun onClickOnWasNotMe() {
sharedViewModel.itWasNotMe() // sharedViewModel.itWasNotMe()
} // }
//
override fun acceptRequest() { // override fun acceptRequest() {
sharedViewModel.handle(VerificationAction.ReadyPendingVerification) // sharedViewModel.handle(VerificationAction.ReadyPendingVerification)
} // }
//
override fun declineRequest() { // override fun declineRequest() {
sharedViewModel.handle(VerificationAction.CancelPendingVerification) // sharedViewModel.handle(VerificationAction.CancelPendingVerification)
} // }
//
private fun doOpenQRCodeScanner() { // private fun doOpenQRCodeScanner() {
QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher) // QrCodeScannerActivity.startForResult(requireActivity(), scanActivityResultLauncher)
} // }
//
private val scanActivityResultLauncher = registerStartForActivityResult { activityResult -> // private val scanActivityResultLauncher = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) { // if (activityResult.resultCode == Activity.RESULT_OK) {
val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data) // val scannedQrCode = QrCodeScannerActivity.getResultText(activityResult.data)
val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data) // val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(activityResult.data)
//
if (wasQrCode && !scannedQrCode.isNullOrBlank()) { // if (wasQrCode && !scannedQrCode.isNullOrBlank()) {
onRemoteQrCodeScanned(scannedQrCode) // onRemoteQrCodeScanned(scannedQrCode)
} else { // } else {
Timber.w("It was not a QR code, or empty result") // Timber.w("It was not a QR code, or empty result")
} // }
} // }
} // }
//
private fun onRemoteQrCodeScanned(remoteQrCode: String) = withState(sharedViewModel) { state -> // private fun onRemoteQrCodeScanned(remoteQrCode: String) = withState(sharedViewModel) { state ->
sharedViewModel.handle( // sharedViewModel.handle(
VerificationAction.RemoteQrCodeScanned( // VerificationAction.RemoteQrCodeScanned(
state.otherUserId, // state.otherUserId,
state.pendingRequest.invoke()?.transactionId ?: "", // state.pendingRequest.invoke()?.transactionId ?: "",
remoteQrCode // remoteQrCode
) // )
) // )
} // }
} // }

View File

@ -1,172 +1,172 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.choose // package im.vector.app.features.crypto.verification.choose
//
import com.airbnb.mvrx.MavericksState // import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModelFactory // import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.ViewModelContext // import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted // import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory // import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject // import dagger.assisted.AssistedInject
import dagger.hilt.EntryPoints // import dagger.hilt.EntryPoints
import im.vector.app.core.di.MavericksAssistedViewModelFactory // import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.SingletonEntryPoint // import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.core.di.hiltMavericksViewModelFactory // import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyAction // import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents // import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel // import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.crypto.verification.VerificationBottomSheet // import im.vector.app.features.crypto.verification.VerificationBottomSheet
import kotlinx.coroutines.flow.launchIn // import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach // import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch // 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.session.crypto.verification.EVerificationState // import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest // import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction // import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent // import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService // import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction // import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
//
data class VerificationChooseMethodViewState( // data class VerificationChooseMethodViewState(
val otherUserId: String = "", // val otherUserId: String = "",
val transactionId: String = "", // val transactionId: String = "",
val otherCanShowQrCode: Boolean = false, // val otherCanShowQrCode: Boolean = false,
val otherCanScanQrCode: Boolean = false, // val otherCanScanQrCode: Boolean = false,
val qrCodeText: String? = null, // val qrCodeText: String? = null,
val sasModeAvailable: Boolean = false, // val sasModeAvailable: Boolean = false,
val isMe: Boolean = false, // val isMe: Boolean = false,
val canCrossSign: Boolean = false, // val canCrossSign: Boolean = false,
val isReadySent: Boolean = false // val isReadySent: Boolean = false
) : MavericksState // ) : MavericksState
//
class VerificationChooseMethodViewModel @AssistedInject constructor( // class VerificationChooseMethodViewModel @AssistedInject constructor(
@Assisted initialState: VerificationChooseMethodViewState, // @Assisted initialState: VerificationChooseMethodViewState,
private val session: Session // private val session: Session
) : VectorViewModel<VerificationChooseMethodViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener { // ) : VectorViewModel<VerificationChooseMethodViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener {
//
init { // init {
// session.cryptoService().verificationService().addListener(this) // // session.cryptoService().verificationService().addListener(this)
//
session.cryptoService().verificationService() // session.cryptoService().verificationService()
.requestEventFlow() // .requestEventFlow()
.onEach { // .onEach {
when (it) { // when (it) {
// TODO check transaction id // // TODO check transaction id
is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request) // is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request)
is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request) // is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request)
is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction) // is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction)
is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction) // is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction)
} // }
} // }
.launchIn(viewModelScope) // .launchIn(viewModelScope)
//
viewModelScope.launch { // viewModelScope.launch {
//
val verificationService = session.cryptoService().verificationService() // val verificationService = session.cryptoService().verificationService()
val pvr = verificationService.getExistingVerificationRequest(initialState.otherUserId, initialState.transactionId) // val pvr = verificationService.getExistingVerificationRequest(initialState.otherUserId, initialState.transactionId)
//
// Get the QR code now, because transaction is already created, so transactionCreated() will not be called // // Get the QR code now, because transaction is already created, so transactionCreated() will not be called
val qrCodeVerificationTransaction = verificationService.getExistingTransaction(initialState.otherUserId, initialState.transactionId) // val qrCodeVerificationTransaction = verificationService.getExistingTransaction(initialState.otherUserId, initialState.transactionId)
//
setState {
VerificationChooseMethodViewState(
otherUserId = initialState.otherUserId,
isMe = session.myUserId == pvr?.otherUserId,
canCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
transactionId = pvr?.transactionId ?: initialState.transactionId,
otherCanShowQrCode = pvr?.otherCanShowQrCode.orFalse(),
otherCanScanQrCode = pvr?.otherCanScanQrCode.orFalse(),
qrCodeText = pvr?.qrCodeText,
sasModeAvailable = pvr?.isSasSupported.orFalse(),
isReadySent = pvr?.state == EVerificationState.Ready
)
}
}
}
override fun transactionCreated(tx: VerificationTransaction) {
transactionUpdated(tx)
}
override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
// if (tx.transactionId == state.transactionId && tx is QrCodeVerificationTransaction) {
// setState { // setState {
// copy( // VerificationChooseMethodViewState(
// qrCodeText = tx.qrCodeText // otherUserId = initialState.otherUserId,
// isMe = session.myUserId == pvr?.otherUserId,
// canCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
// transactionId = pvr?.transactionId ?: initialState.transactionId,
// otherCanShowQrCode = pvr?.otherCanShowQrCode.orFalse(),
// otherCanScanQrCode = pvr?.otherCanScanQrCode.orFalse(),
// qrCodeText = pvr?.qrCodeText,
// sasModeAvailable = pvr?.isSasSupported.orFalse(),
// isReadySent = pvr?.state == EVerificationState.Ready
// ) // )
// } // }
// } // }
} //
//
override fun verificationRequestCreated(pr: PendingVerificationRequest) { // }
verificationRequestUpdated(pr) //
} // override fun transactionCreated(tx: VerificationTransaction) {
// transactionUpdated(tx)
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state -> // }
viewModelScope.launch { //
val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId) // override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
// // if (tx.transactionId == state.transactionId && tx is QrCodeVerificationTransaction) {
setState { // // setState {
copy( // // copy(
otherCanShowQrCode = pvr?.otherCanShowQrCode.orFalse(), // // qrCodeText = tx.qrCodeText
otherCanScanQrCode = pvr?.otherCanScanQrCode.orFalse(), // // )
sasModeAvailable = pvr?.isSasSupported.orFalse(), // // }
isReadySent = pvr?.state == EVerificationState.Ready, // // }
qrCodeText = pvr?.qrCodeText // }
) //
} // override fun verificationRequestCreated(pr: PendingVerificationRequest) {
} // verificationRequestUpdated(pr)
} // }
//
@AssistedFactory // override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
interface Factory : MavericksAssistedViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> { // viewModelScope.launch {
override fun create(initialState: VerificationChooseMethodViewState): VerificationChooseMethodViewModel // val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
} //
// setState {
companion object : MavericksViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> by hiltMavericksViewModelFactory() { // copy(
// otherCanShowQrCode = pvr?.otherCanShowQrCode.orFalse(),
override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState { // otherCanScanQrCode = pvr?.otherCanScanQrCode.orFalse(),
val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args() // sasModeAvailable = pvr?.isSasSupported.orFalse(),
val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession() // isReadySent = pvr?.state == EVerificationState.Ready,
val verificationService = session.cryptoService().verificationService() // qrCodeText = pvr?.qrCodeText
// val pvr = verificationService.getExistingVerificationRequest(args.otherUserId, args.verificationId) // )
// }
// Get the QR code now, because transaction is already created, so transactionCreated() will not be called // }
// val qrCodeVerificationTransaction = verificationService.getExistingTransaction(args.otherUserId, args.verificationId ?: "") // }
//
return VerificationChooseMethodViewState( // @AssistedFactory
otherUserId = args.otherUserId, // interface Factory : MavericksAssistedViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> {
// isMe = session.myUserId == pvr?.otherUserId, // override fun create(initialState: VerificationChooseMethodViewState): VerificationChooseMethodViewModel
canCrossSign = session.cryptoService().crossSigningService().canCrossSign(), // }
transactionId = args.verificationId ?: "", //
// otherCanShowQrCode = pvr?.otherCanShowQrCode().orFalse(), // companion object : MavericksViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> by hiltMavericksViewModelFactory() {
// otherCanScanQrCode = pvr?.otherCanScanQrCode().orFalse(), //
// qrCodeText = (qrCodeVerificationTransaction as? QrCodeVerificationTransaction)?.qrCodeText, // override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState {
// sasModeAvailable = pvr?.isSasSupported().orFalse() // val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args()
) // val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
} // val verificationService = session.cryptoService().verificationService()
} // // val pvr = verificationService.getExistingVerificationRequest(args.otherUserId, args.verificationId)
//
override fun onCleared() { // // Get the QR code now, because transaction is already created, so transactionCreated() will not be called
// session.cryptoService().verificationService().removeListener(this) // // val qrCodeVerificationTransaction = verificationService.getExistingTransaction(args.otherUserId, args.verificationId ?: "")
super.onCleared() //
} // return VerificationChooseMethodViewState(
// otherUserId = args.otherUserId,
override fun handle(action: EmptyAction) {} // // isMe = session.myUserId == pvr?.otherUserId,
} // canCrossSign = session.cryptoService().crossSigningService().canCrossSign(),
// transactionId = args.verificationId ?: "",
// // otherCanShowQrCode = pvr?.otherCanShowQrCode().orFalse(),
// // otherCanScanQrCode = pvr?.otherCanScanQrCode().orFalse(),
// // qrCodeText = (qrCodeVerificationTransaction as? QrCodeVerificationTransaction)?.qrCodeText,
// // sasModeAvailable = pvr?.isSasSupported().orFalse()
// )
// }
// }
//
// override fun onCleared() {
// // session.cryptoService().verificationService().removeListener(this)
// super.onCleared()
// }
//
// override fun handle(action: EmptyAction) {}
// }

View File

@ -1,143 +1,143 @@
/* // /*
* Copyright 2020 New Vector Ltd // * Copyright 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.conclusion // package im.vector.app.features.crypto.verification.conclusion
//
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.html.EventHtmlRenderer // import im.vector.app.features.html.EventHtmlRenderer
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel // import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationConclusionController @Inject constructor( // class VerificationConclusionController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider, // private val colorProvider: ColorProvider,
private val eventHtmlRenderer: EventHtmlRenderer // private val eventHtmlRenderer: EventHtmlRenderer
) : EpoxyController() { // ) : EpoxyController() {
//
var listener: Listener? = null // var listener: Listener? = null
//
private var viewState: VerificationConclusionViewState? = null // private var viewState: VerificationConclusionViewState? = null
//
fun update(viewState: VerificationConclusionViewState) { // fun update(viewState: VerificationConclusionViewState) {
this.viewState = viewState // this.viewState = viewState
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val state = viewState ?: return // val state = viewState ?: return
val host = this // val host = this
//
when (state.conclusionState) { // when (state.conclusionState) {
ConclusionState.SUCCESS -> { // ConclusionState.SUCCESS -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice( // notice(
host.stringProvider.getString( // host.stringProvider.getString(
if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice // if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice
else R.string.verification_conclusion_ok_notice // else R.string.verification_conclusion_ok_notice
) // )
.toEpoxyCharSequence() // .toEpoxyCharSequence()
) // )
} // }
//
bottomSheetVerificationBigImageItem { // bottomSheetVerificationBigImageItem {
id("image") // id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted) // roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
} // }
//
bottomDone() // bottomDone()
} // }
ConclusionState.WARNING -> { // ConclusionState.WARNING -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure).toEpoxyCharSequence())
} // }
//
bottomSheetVerificationBigImageItem { // bottomSheetVerificationBigImageItem {
id("image") // id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Warning) // roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Warning)
} // }
//
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("warning_notice") // id("warning_notice")
notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised)).toEpoxyCharSequence()) // notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised)).toEpoxyCharSequence())
} // }
//
bottomGotIt() // bottomGotIt()
} // }
ConclusionState.INVALID_QR_CODE -> { // ConclusionState.INVALID_QR_CODE -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("invalid_qr") // id("invalid_qr")
notice(host.stringProvider.getString(R.string.verify_invalid_qr_notice).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verify_invalid_qr_notice).toEpoxyCharSequence())
} // }
//
bottomGotIt() // bottomGotIt()
} // }
ConclusionState.CANCELLED -> { // ConclusionState.CANCELLED -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice_cancelled") // id("notice_cancelled")
notice(host.stringProvider.getString(R.string.verify_cancelled_notice).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verify_cancelled_notice).toEpoxyCharSequence())
} // }
//
bottomGotIt() // bottomGotIt()
} // }
} // }
} // }
//
private fun bottomDone() { // private fun bottomDone() {
val host = this // val host = this
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("done") // id("done")
title(host.stringProvider.getString(R.string.done)) // title(host.stringProvider.getString(R.string.done))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onButtonTapped(true) } // listener { host.listener?.onButtonTapped(true) }
} // }
} // }
//
private fun bottomGotIt() { // private fun bottomGotIt() {
val host = this // val host = this
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("got_it") // id("got_it")
title(host.stringProvider.getString(R.string.sas_got_it)) // title(host.stringProvider.getString(R.string.sas_got_it))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onButtonTapped(false) } // listener { host.listener?.onButtonTapped(false) }
} // }
} // }
//
interface Listener { // interface Listener {
fun onButtonTapped(success: Boolean) // fun onButtonTapped(success: Boolean)
} // }
} // }

View File

@ -1,82 +1,82 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.conclusion // package im.vector.app.features.crypto.verification.conclusion
//
import android.os.Bundle // import android.os.Bundle
import android.os.Parcelable // import android.os.Parcelable
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel // import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.cleanup // import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith // import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction // import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import kotlinx.parcelize.Parcelize // import kotlinx.parcelize.Parcelize
import javax.inject.Inject // import javax.inject.Inject
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationConclusionFragment : // class VerificationConclusionFragment :
VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(), // VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationConclusionController.Listener { // VerificationConclusionController.Listener {
//
@Inject lateinit var controller: VerificationConclusionController // @Inject lateinit var controller: VerificationConclusionController
//
@Parcelize // @Parcelize
data class Args( // data class Args(
val isSuccessFull: Boolean, // val isSuccessFull: Boolean,
val cancelReason: String?, // val cancelReason: String?,
val isMe: Boolean // val isMe: Boolean
) : Parcelable // ) : Parcelable
//
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) // private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
//
private val viewModel by fragmentViewModel(VerificationConclusionViewModel::class) // private val viewModel by fragmentViewModel(VerificationConclusionViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
//
setupRecyclerView() // setupRecyclerView()
} // }
//
override fun onDestroyView() { // override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup() // views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null // controller.listener = null
super.onDestroyView() // super.onDestroyView()
} // }
//
private fun setupRecyclerView() { // private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) // views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this // controller.listener = this
} // }
//
override fun invalidate() = withState(viewModel) { state -> // override fun invalidate() = withState(viewModel) { state ->
controller.update(state) // controller.update(state)
} // }
//
override fun onButtonTapped(success: Boolean) { // override fun onButtonTapped(success: Boolean) {
sharedViewModel.handle(VerificationAction.GotItConclusion(success)) // sharedViewModel.handle(VerificationAction.GotItConclusion(success))
} // }
} // }

View File

@ -1,68 +1,68 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.conclusion // package im.vector.app.features.crypto.verification.conclusion
//
import com.airbnb.mvrx.MavericksState // import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModelFactory // import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.ViewModelContext // import com.airbnb.mvrx.ViewModelContext
import im.vector.app.core.platform.EmptyAction // import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents // import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel // import im.vector.app.core.platform.VectorViewModel
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode // import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf // import org.matrix.android.sdk.api.session.crypto.verification.safeValueOf
//
data class VerificationConclusionViewState( // data class VerificationConclusionViewState(
val conclusionState: ConclusionState = ConclusionState.CANCELLED, // val conclusionState: ConclusionState = ConclusionState.CANCELLED,
val isSelfVerification: Boolean = false // val isSelfVerification: Boolean = false
) : MavericksState // ) : MavericksState
//
enum class ConclusionState { // enum class ConclusionState {
SUCCESS, // SUCCESS,
WARNING, // WARNING,
CANCELLED, // CANCELLED,
INVALID_QR_CODE // INVALID_QR_CODE
} // }
//
class VerificationConclusionViewModel(initialState: VerificationConclusionViewState) : // class VerificationConclusionViewModel(initialState: VerificationConclusionViewState) :
VectorViewModel<VerificationConclusionViewState, EmptyAction, EmptyViewEvents>(initialState) { // VectorViewModel<VerificationConclusionViewState, EmptyAction, EmptyViewEvents>(initialState) {
//
companion object : MavericksViewModelFactory<VerificationConclusionViewModel, VerificationConclusionViewState> { // companion object : MavericksViewModelFactory<VerificationConclusionViewModel, VerificationConclusionViewState> {
//
override fun initialState(viewModelContext: ViewModelContext): VerificationConclusionViewState? { // override fun initialState(viewModelContext: ViewModelContext): VerificationConclusionViewState? {
val args = viewModelContext.args<VerificationConclusionFragment.Args>() // val args = viewModelContext.args<VerificationConclusionFragment.Args>()
//
return when (safeValueOf(args.cancelReason)) { // return when (safeValueOf(args.cancelReason)) {
CancelCode.QrCodeInvalid -> { // CancelCode.QrCodeInvalid -> {
VerificationConclusionViewState(ConclusionState.INVALID_QR_CODE, args.isMe) // VerificationConclusionViewState(ConclusionState.INVALID_QR_CODE, args.isMe)
} // }
CancelCode.MismatchedUser, // CancelCode.MismatchedUser,
CancelCode.MismatchedSas, // CancelCode.MismatchedSas,
CancelCode.MismatchedCommitment, // CancelCode.MismatchedCommitment,
CancelCode.MismatchedKeys -> { // CancelCode.MismatchedKeys -> {
VerificationConclusionViewState(ConclusionState.WARNING, args.isMe) // VerificationConclusionViewState(ConclusionState.WARNING, args.isMe)
} // }
else -> { // else -> {
VerificationConclusionViewState( // VerificationConclusionViewState(
if (args.isSuccessFull) ConclusionState.SUCCESS else ConclusionState.CANCELLED, // if (args.isSuccessFull) ConclusionState.SUCCESS else ConclusionState.CANCELLED,
args.isMe // args.isMe
) // )
} // }
} // }
} // }
} // }
//
override fun handle(action: EmptyAction) {} // override fun handle(action: EmptyAction) {}
} // }

View File

@ -1,168 +1,168 @@
/* // /*
* Copyright 2020 New Vector Ltd // * Copyright 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.emoji // package im.vector.app.features.crypto.verification.emoji
//
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.Fail // import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Success // import com.airbnb.mvrx.Success
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.epoxy.errorWithRetryItem // import im.vector.app.core.epoxy.errorWithRetryItem
import im.vector.app.core.error.ErrorFormatter // import im.vector.app.core.error.ErrorFormatter
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationDecimalCodeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationDecimalCodeItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationEmojisItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationEmojisItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
import im.vector.app.features.displayname.getBestName // import im.vector.app.features.displayname.getBestName
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationEmojiCodeController @Inject constructor( // class VerificationEmojiCodeController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider, // private val colorProvider: ColorProvider,
private val errorFormatter: ErrorFormatter // private val errorFormatter: ErrorFormatter
) : EpoxyController() { // ) : EpoxyController() {
//
var listener: Listener? = null // var listener: Listener? = null
//
private var viewState: VerificationEmojiCodeViewState? = null // private var viewState: VerificationEmojiCodeViewState? = null
//
fun update(viewState: VerificationEmojiCodeViewState) { // fun update(viewState: VerificationEmojiCodeViewState) {
this.viewState = viewState // this.viewState = viewState
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val state = viewState ?: return // val state = viewState ?: return
//
if (state.supportsEmoji) { // if (state.supportsEmoji) {
buildEmojiItem(state) // buildEmojiItem(state)
} else { // } else {
buildDecimal(state) // buildDecimal(state)
} // }
} // }
//
private fun buildEmojiItem(state: VerificationEmojiCodeViewState) { // private fun buildEmojiItem(state: VerificationEmojiCodeViewState) {
val host = this // val host = this
when (val emojiDescription = state.emojiDescription) { // when (val emojiDescription = state.emojiDescription) {
is Success -> { // is Success -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verification_emoji_notice).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verification_emoji_notice).toEpoxyCharSequence())
} // }
//
bottomSheetVerificationEmojisItem { // bottomSheetVerificationEmojisItem {
id("emojis") // id("emojis")
emojiRepresentation0(emojiDescription()[0]) // emojiRepresentation0(emojiDescription()[0])
emojiRepresentation1(emojiDescription()[1]) // emojiRepresentation1(emojiDescription()[1])
emojiRepresentation2(emojiDescription()[2]) // emojiRepresentation2(emojiDescription()[2])
emojiRepresentation3(emojiDescription()[3]) // emojiRepresentation3(emojiDescription()[3])
emojiRepresentation4(emojiDescription()[4]) // emojiRepresentation4(emojiDescription()[4])
emojiRepresentation5(emojiDescription()[5]) // emojiRepresentation5(emojiDescription()[5])
emojiRepresentation6(emojiDescription()[6]) // emojiRepresentation6(emojiDescription()[6])
} // }
//
buildActions(state) // buildActions(state)
} // }
is Fail -> { // is Fail -> {
errorWithRetryItem { // errorWithRetryItem {
id("error") // id("error")
text(host.errorFormatter.toHumanReadable(emojiDescription.error)) // text(host.errorFormatter.toHumanReadable(emojiDescription.error))
} // }
} // }
else -> { // else -> {
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.please_wait)) // title(host.stringProvider.getString(R.string.please_wait))
} // }
} // }
} // }
} // }
//
private fun buildDecimal(state: VerificationEmojiCodeViewState) { // private fun buildDecimal(state: VerificationEmojiCodeViewState) {
val host = this // val host = this
when (val decimalDescription = state.decimalDescription) { // when (val decimalDescription = state.decimalDescription) {
is Success -> { // is Success -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verification_code_notice).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verification_code_notice).toEpoxyCharSequence())
} // }
//
bottomSheetVerificationDecimalCodeItem { // bottomSheetVerificationDecimalCodeItem {
id("decimal") // id("decimal")
code(state.decimalDescription.invoke() ?: "") // code(state.decimalDescription.invoke() ?: "")
} // }
//
buildActions(state) // buildActions(state)
} // }
is Fail -> { // is Fail -> {
errorWithRetryItem { // errorWithRetryItem {
id("error") // id("error")
text(host.errorFormatter.toHumanReadable(decimalDescription.error)) // text(host.errorFormatter.toHumanReadable(decimalDescription.error))
} // }
} // }
else -> { // else -> {
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.please_wait)) // title(host.stringProvider.getString(R.string.please_wait))
} // }
} // }
} // }
} // }
//
private fun buildActions(state: VerificationEmojiCodeViewState) { // private fun buildActions(state: VerificationEmojiCodeViewState) {
val host = this // val host = this
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
//
if (state.isWaitingFromOther) { // if (state.isWaitingFromOther) {
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUser.getBestName())) // title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUser.getBestName()))
} // }
} else { // } else {
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("ko") // id("ko")
title(host.stringProvider.getString(R.string.verification_sas_do_not_match)) // title(host.stringProvider.getString(R.string.verification_sas_do_not_match))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
iconRes(R.drawable.ic_check_off) // iconRes(R.drawable.ic_check_off)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
listener { host.listener?.onDoNotMatchButtonTapped() } // listener { host.listener?.onDoNotMatchButtonTapped() }
} // }
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep1") // id("sep1")
} // }
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("ok") // id("ok")
title(host.stringProvider.getString(R.string.verification_sas_match)) // title(host.stringProvider.getString(R.string.verification_sas_match))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_check_on) // iconRes(R.drawable.ic_check_on)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener { host.listener?.onMatchButtonTapped() } // listener { host.listener?.onMatchButtonTapped() }
} // }
} // }
} // }
//
interface Listener { // interface Listener {
fun onDoNotMatchButtonTapped() // fun onDoNotMatchButtonTapped()
fun onMatchButtonTapped() // fun onMatchButtonTapped()
} // }
} // }

View File

@ -1,81 +1,81 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.emoji // package im.vector.app.features.crypto.verification.emoji
//
import android.os.Bundle // import android.os.Bundle
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.fragmentViewModel // import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.cleanup // import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith // import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction // import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import javax.inject.Inject // import javax.inject.Inject
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationEmojiCodeFragment : // class VerificationEmojiCodeFragment :
VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(), // VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationEmojiCodeController.Listener { // VerificationEmojiCodeController.Listener {
//
@Inject lateinit var controller: VerificationEmojiCodeController // @Inject lateinit var controller: VerificationEmojiCodeController
//
private val viewModel by fragmentViewModel(VerificationEmojiCodeViewModel::class) // private val viewModel by fragmentViewModel(VerificationEmojiCodeViewModel::class)
//
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) // private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
//
setupRecyclerView() // setupRecyclerView()
} // }
//
override fun onDestroyView() { // override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup() // views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null // controller.listener = null
super.onDestroyView() // super.onDestroyView()
} // }
//
private fun setupRecyclerView() { // private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) // views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this // controller.listener = this
} // }
//
override fun invalidate() = withState(viewModel) { state -> // override fun invalidate() = withState(viewModel) { state ->
controller.update(state) // controller.update(state)
} // }
//
override fun onMatchButtonTapped() = withState(viewModel) { state -> // override fun onMatchButtonTapped() = withState(viewModel) { state ->
val otherUserId = state.otherUser.id // val otherUserId = state.otherUser.id
val txId = state.transactionId ?: return@withState // val txId = state.transactionId ?: return@withState
sharedViewModel.handle(VerificationAction.SASMatchAction) // sharedViewModel.handle(VerificationAction.SASMatchAction)
} // }
//
override fun onDoNotMatchButtonTapped() = withState(viewModel) { state -> // override fun onDoNotMatchButtonTapped() = withState(viewModel) { state ->
val otherUserId = state.otherUser.id // val otherUserId = state.otherUser.id
val txId = state.transactionId ?: return@withState // val txId = state.transactionId ?: return@withState
sharedViewModel.handle(VerificationAction.SASDoNotMatchAction) // sharedViewModel.handle(VerificationAction.SASDoNotMatchAction)
} // }
} // }

View File

@ -1,209 +1,209 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.emoji // package im.vector.app.features.crypto.verification.emoji
//
import com.airbnb.mvrx.Async // import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail // import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading // import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksState // import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModelFactory // import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success // import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized // import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext // import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted // import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory // import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject // import dagger.assisted.AssistedInject
import dagger.hilt.EntryPoints // import dagger.hilt.EntryPoints
import im.vector.app.core.di.MavericksAssistedViewModelFactory // import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.SingletonEntryPoint // import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.core.di.hiltMavericksViewModelFactory // import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyAction // import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents // import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel // import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.crypto.verification.VerificationBottomSheet // import im.vector.app.features.crypto.verification.VerificationBottomSheet
import kotlinx.coroutines.flow.launchIn // import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach // import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch // import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.Session // import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation // import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction // import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent // import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService // import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction // import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState // import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.getUserOrDefault // import org.matrix.android.sdk.api.session.getUserOrDefault
import org.matrix.android.sdk.api.util.MatrixItem // import org.matrix.android.sdk.api.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem // import org.matrix.android.sdk.api.util.toMatrixItem
//
data class VerificationEmojiCodeViewState( // data class VerificationEmojiCodeViewState(
val transactionId: String?, // val transactionId: String?,
val otherUser: MatrixItem, // val otherUser: MatrixItem,
val supportsEmoji: Boolean = true, // val supportsEmoji: Boolean = true,
val emojiDescription: Async<List<EmojiRepresentation>> = Uninitialized, // val emojiDescription: Async<List<EmojiRepresentation>> = Uninitialized,
val decimalDescription: Async<String> = Uninitialized, // val decimalDescription: Async<String> = Uninitialized,
val isWaitingFromOther: Boolean = false // val isWaitingFromOther: Boolean = false
) : MavericksState // ) : MavericksState
//
class VerificationEmojiCodeViewModel @AssistedInject constructor( // class VerificationEmojiCodeViewModel @AssistedInject constructor(
@Assisted initialState: VerificationEmojiCodeViewState, // @Assisted initialState: VerificationEmojiCodeViewState,
private val session: Session // private val session: Session
) : VectorViewModel<VerificationEmojiCodeViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener { // ) : VectorViewModel<VerificationEmojiCodeViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener {
//
init { // init {
//
session.cryptoService().verificationService() // session.cryptoService().verificationService()
.requestEventFlow() // .requestEventFlow()
.onEach { // .onEach {
when (it) { // when (it) {
is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request) // is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request)
is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request) // is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request)
is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction) // is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction)
is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction) // is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction)
} // }
} // }
.launchIn(viewModelScope) // .launchIn(viewModelScope)
//
viewModelScope.launch { // viewModelScope.launch {
refreshStateFromTx( // refreshStateFromTx(
session.cryptoService().verificationService() // session.cryptoService().verificationService()
.getExistingTransaction( // .getExistingTransaction(
otherUserId = initialState.otherUser.id, // otherUserId = initialState.otherUser.id,
tid = initialState.transactionId ?: "" // tid = initialState.transactionId ?: ""
) as? SasVerificationTransaction // ) as? SasVerificationTransaction
) // )
//
} // }
//
// session.cryptoService().verificationService().addListener(this) // // session.cryptoService().verificationService().addListener(this)
} // }
//
// override fun onCleared() { // // override fun onCleared() {
// session.cryptoService().verificationService().removeListener(this) // // session.cryptoService().verificationService().removeListener(this)
// super.onCleared() // // super.onCleared()
// // }
//
// private fun refreshStateFromTx(sasTx: SasVerificationTransaction?) {
// when (val state = sasTx?.state) {
// is VerificationTxState.None,
// is VerificationTxState.SasStarted,
// is VerificationTxState.SasAccepted,
// is VerificationTxState.SasKeySent -> {
// setState {
// copy(
// isWaitingFromOther = false,
// supportsEmoji = sasTx.supportsEmoji(),
// emojiDescription = Loading<List<EmojiRepresentation>>()
// .takeIf { sasTx.supportsEmoji() }
// ?: Uninitialized,
// decimalDescription = Loading<String>()
// .takeIf { sasTx.supportsEmoji().not() }
// ?: Uninitialized
// )
// }
// }
// is VerificationTxState.SasShortCodeReady -> {
// setState {
// copy(
// isWaitingFromOther = false,
// supportsEmoji = sasTx.supportsEmoji(),
// emojiDescription = if (sasTx.supportsEmoji()) Success(sasTx.getEmojiCodeRepresentation())
// else Uninitialized,
// decimalDescription = if (!sasTx.supportsEmoji()) Success(sasTx.getDecimalCodeRepresentation().orEmpty())
// else Uninitialized
// )
// }
// }
// is VerificationTxState.SasMacReceived -> {
// if (state.codeConfirmed) {
// setState {
// copy(isWaitingFromOther = true)
// }
// } else {
// setState {
// copy(
// isWaitingFromOther = false,
// supportsEmoji = sasTx.supportsEmoji(),
// emojiDescription = if (sasTx.supportsEmoji()) Success(sasTx.getEmojiCodeRepresentation())
// else Uninitialized,
// decimalDescription = if (!sasTx.supportsEmoji()) Success(sasTx.getDecimalCodeRepresentation().orEmpty())
// else Uninitialized
// )
// }
// }
// }
// is VerificationTxState.SasMacSent,
// is VerificationTxState.Verified -> {
// setState {
// copy(isWaitingFromOther = true)
// }
// }
// is VerificationTxState.Cancelled -> {
// // The fragment should not be rendered in this state,
// // it should have been replaced by a conclusion fragment
// setState {
// copy(
// isWaitingFromOther = false,
// supportsEmoji = sasTx.supportsEmoji(),
// emojiDescription = Fail(Throwable("Transaction Cancelled")),
// decimalDescription = Fail(Throwable("Transaction Cancelled"))
// )
// }
// }
// null -> {
// setState {
// copy(
// isWaitingFromOther = false,
// emojiDescription = Fail(Throwable("Unknown Transaction")),
// decimalDescription = Fail(Throwable("Unknown Transaction"))
// )
// }
// }
// else -> Unit
// }
// }
//
// override fun transactionCreated(tx: VerificationTransaction) {
// transactionUpdated(tx)
// }
//
// override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
// if (tx.transactionId == state.transactionId && tx is SasVerificationTransaction) {
// refreshStateFromTx(tx)
// }
// }
//
// @AssistedFactory
// interface Factory : MavericksAssistedViewModelFactory<VerificationEmojiCodeViewModel, VerificationEmojiCodeViewState> {
// override fun create(initialState: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel
// }
//
// companion object : MavericksViewModelFactory<VerificationEmojiCodeViewModel, VerificationEmojiCodeViewState> by hiltMavericksViewModelFactory() {
//
// override fun initialState(viewModelContext: ViewModelContext): VerificationEmojiCodeViewState {
// val args = viewModelContext.args<VerificationBottomSheet.VerificationArgs>()
// val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
// val matrixItem = session.getUserOrDefault(args.otherUserId).toMatrixItem()
//
// return VerificationEmojiCodeViewState(
// transactionId = args.verificationId,
// otherUser = matrixItem
// )
// }
// }
//
// override fun handle(action: EmptyAction) {
// }
// } // }
private fun refreshStateFromTx(sasTx: SasVerificationTransaction?) {
when (val state = sasTx?.state) {
is VerificationTxState.None,
is VerificationTxState.SasStarted,
is VerificationTxState.SasAccepted,
is VerificationTxState.SasKeySent -> {
setState {
copy(
isWaitingFromOther = false,
supportsEmoji = sasTx.supportsEmoji(),
emojiDescription = Loading<List<EmojiRepresentation>>()
.takeIf { sasTx.supportsEmoji() }
?: Uninitialized,
decimalDescription = Loading<String>()
.takeIf { sasTx.supportsEmoji().not() }
?: Uninitialized
)
}
}
is VerificationTxState.SasShortCodeReady -> {
setState {
copy(
isWaitingFromOther = false,
supportsEmoji = sasTx.supportsEmoji(),
emojiDescription = if (sasTx.supportsEmoji()) Success(sasTx.getEmojiCodeRepresentation())
else Uninitialized,
decimalDescription = if (!sasTx.supportsEmoji()) Success(sasTx.getDecimalCodeRepresentation().orEmpty())
else Uninitialized
)
}
}
is VerificationTxState.SasMacReceived -> {
if (state.codeConfirmed) {
setState {
copy(isWaitingFromOther = true)
}
} else {
setState {
copy(
isWaitingFromOther = false,
supportsEmoji = sasTx.supportsEmoji(),
emojiDescription = if (sasTx.supportsEmoji()) Success(sasTx.getEmojiCodeRepresentation())
else Uninitialized,
decimalDescription = if (!sasTx.supportsEmoji()) Success(sasTx.getDecimalCodeRepresentation().orEmpty())
else Uninitialized
)
}
}
}
is VerificationTxState.SasMacSent,
is VerificationTxState.Verified -> {
setState {
copy(isWaitingFromOther = true)
}
}
is VerificationTxState.Cancelled -> {
// The fragment should not be rendered in this state,
// it should have been replaced by a conclusion fragment
setState {
copy(
isWaitingFromOther = false,
supportsEmoji = sasTx.supportsEmoji(),
emojiDescription = Fail(Throwable("Transaction Cancelled")),
decimalDescription = Fail(Throwable("Transaction Cancelled"))
)
}
}
null -> {
setState {
copy(
isWaitingFromOther = false,
emojiDescription = Fail(Throwable("Unknown Transaction")),
decimalDescription = Fail(Throwable("Unknown Transaction"))
)
}
}
else -> Unit
}
}
override fun transactionCreated(tx: VerificationTransaction) {
transactionUpdated(tx)
}
override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
if (tx.transactionId == state.transactionId && tx is SasVerificationTransaction) {
refreshStateFromTx(tx)
}
}
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<VerificationEmojiCodeViewModel, VerificationEmojiCodeViewState> {
override fun create(initialState: VerificationEmojiCodeViewState): VerificationEmojiCodeViewModel
}
companion object : MavericksViewModelFactory<VerificationEmojiCodeViewModel, VerificationEmojiCodeViewState> by hiltMavericksViewModelFactory() {
override fun initialState(viewModelContext: ViewModelContext): VerificationEmojiCodeViewState {
val args = viewModelContext.args<VerificationBottomSheet.VerificationArgs>()
val session = EntryPoints.get(viewModelContext.app(), SingletonEntryPoint::class.java).activeSessionHolder().getActiveSession()
val matrixItem = session.getUserOrDefault(args.otherUserId).toMatrixItem()
return VerificationEmojiCodeViewState(
transactionId = args.verificationId,
otherUser = matrixItem
)
}
}
override fun handle(action: EmptyAction) {
}
}

View File

@ -1,99 +1,99 @@
/* // /*
* Copyright 2020 New Vector Ltd // * Copyright 2020 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.qrconfirmation // package im.vector.app.features.crypto.verification.qrconfirmation
//
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import im.vector.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.displayname.getBestName // import im.vector.app.features.displayname.getBestName
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel // import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationQrScannedByOtherController @Inject constructor( // class VerificationQrScannedByOtherController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider // private val colorProvider: ColorProvider
) : EpoxyController() { // ) : EpoxyController() {
//
var listener: Listener? = null // var listener: Listener? = null
//
private var viewState: VerificationBottomSheetViewState? = null // private var viewState: VerificationBottomSheetViewState? = null
//
fun update(viewState: VerificationBottomSheetViewState) { // fun update(viewState: VerificationBottomSheetViewState) {
this.viewState = viewState // this.viewState = viewState
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val state = viewState ?: return // val state = viewState ?: return
val host = this // val host = this
//
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
apply { // apply {
if (state.isMe) { // if (state.isMe) {
notice(host.stringProvider.getString(R.string.qr_code_scanned_self_verif_notice).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.qr_code_scanned_self_verif_notice).toEpoxyCharSequence())
} else { // } else {
val name = state.otherUserMxItem.getBestName() // val name = state.otherUserMxItem.getBestName()
notice(host.stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name).toEpoxyCharSequence())
} // }
} // }
} // }
//
bottomSheetVerificationBigImageItem { // bottomSheetVerificationBigImageItem {
id("image") // id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted) // roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("deny") // id("deny")
title(host.stringProvider.getString(R.string.qr_code_scanned_by_other_no)) // title(host.stringProvider.getString(R.string.qr_code_scanned_by_other_no))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
iconRes(R.drawable.ic_check_off) // iconRes(R.drawable.ic_check_off)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
listener { host.listener?.onUserDeniesQrCodeScanned() } // listener { host.listener?.onUserDeniesQrCodeScanned() }
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep1") // id("sep1")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("confirm") // id("confirm")
title(host.stringProvider.getString(R.string.qr_code_scanned_by_other_yes)) // title(host.stringProvider.getString(R.string.qr_code_scanned_by_other_yes))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_check_on) // iconRes(R.drawable.ic_check_on)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener { host.listener?.onUserConfirmsQrCodeScanned() } // listener { host.listener?.onUserConfirmsQrCodeScanned() }
} // }
} // }
//
interface Listener { // interface Listener {
fun onUserConfirmsQrCodeScanned() // fun onUserConfirmsQrCodeScanned()
fun onUserDeniesQrCodeScanned() // fun onUserDeniesQrCodeScanned()
} // }
} // }

View File

@ -1,73 +1,73 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.qrconfirmation // package im.vector.app.features.crypto.verification.qrconfirmation
//
import android.os.Bundle // import android.os.Bundle
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.cleanup // import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith // import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction // import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import javax.inject.Inject // import javax.inject.Inject
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationQrScannedByOtherFragment : // class VerificationQrScannedByOtherFragment :
VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(), // VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationQrScannedByOtherController.Listener { // VerificationQrScannedByOtherController.Listener {
//
@Inject lateinit var controller: VerificationQrScannedByOtherController // @Inject lateinit var controller: VerificationQrScannedByOtherController
//
private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) // private val sharedViewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
setupRecyclerView() // setupRecyclerView()
} // }
//
override fun invalidate() = withState(sharedViewModel) { state -> // override fun invalidate() = withState(sharedViewModel) { state ->
controller.update(state) // controller.update(state)
} // }
//
override fun onDestroyView() { // override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup() // views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null // controller.listener = null
super.onDestroyView() // super.onDestroyView()
} // }
//
private fun setupRecyclerView() { // private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) // views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this // controller.listener = this
} // }
//
override fun onUserConfirmsQrCodeScanned() { // override fun onUserConfirmsQrCodeScanned() {
sharedViewModel.handle(VerificationAction.OtherUserScannedSuccessfully) // sharedViewModel.handle(VerificationAction.OtherUserScannedSuccessfully)
} // }
//
override fun onUserDeniesQrCodeScanned() { // override fun onUserDeniesQrCodeScanned() {
sharedViewModel.handle(VerificationAction.OtherUserDidNotScanned) // sharedViewModel.handle(VerificationAction.OtherUserDidNotScanned)
} // }
} // }

View File

@ -1,185 +1,185 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.request // package im.vector.app.features.crypto.verification.request
//
import androidx.core.text.toSpannable // import androidx.core.text.toSpannable
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.Fail // 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.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.colorizeMatchingText // import im.vector.app.core.utils.colorizeMatchingText
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewState
import im.vector.app.features.crypto.verification.epoxy.bottomSheetSelfWaitItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetSelfWaitItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
import im.vector.app.features.displayname.getBestName // import im.vector.app.features.displayname.getBestName
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState // import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationRequestController @Inject constructor( // class VerificationRequestController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider // private val colorProvider: ColorProvider
) : EpoxyController() { // ) : EpoxyController() {
//
var listener: Listener? = null // var listener: Listener? = null
//
private var viewState: VerificationBottomSheetViewState? = null // private var viewState: VerificationBottomSheetViewState? = null
//
fun update(viewState: VerificationBottomSheetViewState) { // fun update(viewState: VerificationBottomSheetViewState) {
this.viewState = viewState // this.viewState = viewState
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val state = viewState ?: return // val state = viewState ?: return
val host = this // val host = this
//
if (state.selfVerificationMode) { // if (state.selfVerificationMode) {
if (state.hasAnyOtherSession) { // if (state.hasAnyOtherSession) {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verification_open_other_to_verify).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verification_open_other_to_verify).toEpoxyCharSequence())
} // }
//
bottomSheetSelfWaitItem { // bottomSheetSelfWaitItem {
id("waiting") // id("waiting")
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep") // id("sep")
} // }
} // }
//
if (state.quadSContainsSecrets) { // if (state.quadSContainsSecrets) {
val subtitle = if (state.hasAnyOtherSession) { // val subtitle = if (state.hasAnyOtherSession) {
stringProvider.getString(R.string.verification_use_passphrase) // stringProvider.getString(R.string.verification_use_passphrase)
} else { // } else {
null // null
} // }
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("passphrase") // id("passphrase")
title(host.stringProvider.getString(R.string.verification_cannot_access_other_session)) // title(host.stringProvider.getString(R.string.verification_cannot_access_other_session))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
subTitle(subtitle) // subTitle(subtitle)
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onClickRecoverFromPassphrase() } // listener { host.listener?.onClickRecoverFromPassphrase() }
} // }
} // }
//
if (!state.isVerificationRequired) { // if (!state.isVerificationRequired) {
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep1") // id("sep1")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("skip") // id("skip")
title(host.stringProvider.getString(R.string.action_skip)) // title(host.stringProvider.getString(R.string.action_skip))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
listener { host.listener?.onClickSkip() } // listener { host.listener?.onClickSkip() }
} // }
} // }
} else { // } else {
val styledText = // val styledText =
if (state.isMe) { // if (state.isMe) {
stringProvider.getString(R.string.verify_new_session_notice) // stringProvider.getString(R.string.verify_new_session_notice)
} else { // } else {
stringProvider.getString(R.string.verification_request_notice, state.otherUserId) // stringProvider.getString(R.string.verification_request_notice, state.otherUserId)
.toSpannable() // .toSpannable()
.colorizeMatchingText(state.otherUserId, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color)) // .colorizeMatchingText(state.otherUserId, colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
} // }
//
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(styledText.toEpoxyCharSequence()) // notice(styledText.toEpoxyCharSequence())
} // }
//
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep") // id("sep")
} // }
//
when (val pr = state.pendingRequest) { // when (val pr = state.pendingRequest) {
is Uninitialized -> { // is Uninitialized -> {
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("start") // id("start")
title(host.stringProvider.getString(R.string.start_verification)) // title(host.stringProvider.getString(R.string.start_verification))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
subTitle(host.stringProvider.getString(R.string.verification_request_start_notice)) // subTitle(host.stringProvider.getString(R.string.verification_request_start_notice))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onClickOnVerificationStart() } // listener { host.listener?.onClickOnVerificationStart() }
} // }
} // }
is Loading -> { // is Loading -> {
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUserMxItem.getBestName())) // title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUserMxItem.getBestName()))
} // }
} // }
is Success -> { // is Success -> {
if (pr.invoke().state != EVerificationState.Ready) { // if (pr.invoke().state != EVerificationState.Ready) {
if (state.isMe) { // if (state.isMe) {
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.verification_request_waiting)) // title(host.stringProvider.getString(R.string.verification_request_waiting))
} // }
} else { // } else {
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUserMxItem.getBestName())) // title(host.stringProvider.getString(R.string.verification_request_waiting_for, state.otherUserMxItem.getBestName()))
} // }
} // }
} // }
} // }
is Fail -> Unit // is Fail -> Unit
} // }
} // }
//
if (state.isMe && state.currentDeviceCanCrossSign && !state.selfVerificationMode) { // if (state.isMe && state.currentDeviceCanCrossSign && !state.selfVerificationMode) {
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep_notMe") // id("sep_notMe")
} // }
//
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("wasnote") // id("wasnote")
title(host.stringProvider.getString(R.string.verify_new_session_was_not_me)) // title(host.stringProvider.getString(R.string.verify_new_session_was_not_me))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
subTitle(host.stringProvider.getString(R.string.verify_new_session_compromized)) // subTitle(host.stringProvider.getString(R.string.verify_new_session_compromized))
iconRes(R.drawable.ic_arrow_right) // iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onClickOnWasNotMe() } // listener { host.listener?.onClickOnWasNotMe() }
} // }
} // }
} // }
//
interface Listener { // interface Listener {
fun onClickOnVerificationStart() // fun onClickOnVerificationStart()
fun onClickOnWasNotMe() // fun onClickOnWasNotMe()
fun onClickRecoverFromPassphrase() // fun onClickRecoverFromPassphrase()
fun onClickDismiss() // fun onClickDismiss()
fun onClickSkip() // fun onClickSkip()
} // }
} // }

View File

@ -1,85 +1,85 @@
/* // /*
* Copyright 2019 New Vector Ltd // * Copyright 2019 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
package im.vector.app.features.crypto.verification.request // package im.vector.app.features.crypto.verification.request
//
import android.os.Bundle // import android.os.Bundle
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.View // import android.view.View
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import com.airbnb.mvrx.withState // import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.extensions.cleanup // import im.vector.app.core.extensions.cleanup
import im.vector.app.core.extensions.configureWith // import im.vector.app.core.extensions.configureWith
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.VerificationAction // import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel // import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import javax.inject.Inject // import javax.inject.Inject
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationRequestFragment : // class VerificationRequestFragment :
VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(), // VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>(),
VerificationRequestController.Listener { // VerificationRequestController.Listener {
//
@Inject lateinit var controller: VerificationRequestController // @Inject lateinit var controller: VerificationRequestController
//
private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class) // private val viewModel by parentFragmentViewModel(VerificationBottomSheetViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { // override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) // super.onViewCreated(view, savedInstanceState)
setupRecyclerView() // setupRecyclerView()
} // }
//
override fun onDestroyView() { // override fun onDestroyView() {
views.bottomSheetVerificationRecyclerView.cleanup() // views.bottomSheetVerificationRecyclerView.cleanup()
controller.listener = null // controller.listener = null
super.onDestroyView() // super.onDestroyView()
} // }
//
private fun setupRecyclerView() { // private fun setupRecyclerView() {
views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true) // views.bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
controller.listener = this // controller.listener = this
} // }
//
override fun invalidate() = withState(viewModel) { state -> // override fun invalidate() = withState(viewModel) { state ->
controller.update(state) // controller.update(state)
} // }
//
override fun onClickOnVerificationStart(): Unit = withState(viewModel) { state -> // override fun onClickOnVerificationStart(): Unit = withState(viewModel) { state ->
viewModel.handle(VerificationAction.RequestVerificationByDM) // viewModel.handle(VerificationAction.RequestVerificationByDM)
} // }
//
override fun onClickRecoverFromPassphrase() { // override fun onClickRecoverFromPassphrase() {
viewModel.handle(VerificationAction.VerifyFromPassphrase) // viewModel.handle(VerificationAction.VerifyFromPassphrase)
} // }
//
override fun onClickDismiss() { // override fun onClickDismiss() {
viewModel.handle(VerificationAction.SkipVerification) // viewModel.handle(VerificationAction.SkipVerification)
} // }
//
override fun onClickSkip() { // override fun onClickSkip() {
viewModel.queryCancel() // viewModel.queryCancel()
} // }
//
override fun onClickOnWasNotMe() { // override fun onClickOnWasNotMe() {
viewModel.itWasNotMe() // viewModel.itWasNotMe()
} // }
} // }

View File

@ -1,178 +1,178 @@
/* // /*
* Copyright (c) 2022 New Vector Ltd // * Copyright (c) 2022 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.transaction // package im.vector.app.features.crypto.verification.transaction
//
import com.airbnb.epoxy.EpoxyController // import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.Async // import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail // 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.app.R // import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem // import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider // import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider // import im.vector.app.core.resources.StringProvider
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationEmojisItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationEmojisItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem // import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
import im.vector.app.features.html.EventHtmlRenderer // import im.vector.app.features.html.EventHtmlRenderer
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence // import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel // import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.crypto.verification.CancelCode // import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation // import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction // import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction // import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState // import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import javax.inject.Inject // import javax.inject.Inject
//
class VerificationTransactionController @Inject constructor( // class VerificationTransactionController @Inject constructor(
private val stringProvider: StringProvider, // private val stringProvider: StringProvider,
private val colorProvider: ColorProvider, // private val colorProvider: ColorProvider,
private val eventHtmlRenderer: EventHtmlRenderer, // private val eventHtmlRenderer: EventHtmlRenderer,
) : EpoxyController() { // ) : EpoxyController() {
//
var aTransaction: Async<VerificationTransaction>? = null // var aTransaction: Async<VerificationTransaction>? = null
//
fun update(asyncTransaction: Async<VerificationTransaction>) { // fun update(asyncTransaction: Async<VerificationTransaction>) {
this.aTransaction = asyncTransaction // this.aTransaction = asyncTransaction
requestModelBuild() // requestModelBuild()
} // }
//
override fun buildModels() { // override fun buildModels() {
val host = this // val host = this
when (aTransaction) { // when (aTransaction) {
null, // null,
Uninitialized -> { // Uninitialized -> {
// empty // // empty
} // }
is Fail -> { // is Fail -> {
} // }
is Loading -> { // is Loading -> {
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.please_wait)) // title(host.stringProvider.getString(R.string.please_wait))
} // }
} // }
is Success -> { // is Success -> {
val tx = aTransaction?.invoke() ?: return // val tx = aTransaction?.invoke() ?: return
if (tx is SasVerificationTransaction) { // if (tx is SasVerificationTransaction) {
when (val txState = tx.state) { // when (val txState = tx.state) {
VerificationTxState.SasShortCodeReady -> { // VerificationTxState.SasShortCodeReady -> {
buildEmojiItem(tx.getEmojiCodeRepresentation()) // buildEmojiItem(tx.getEmojiCodeRepresentation())
} // }
is VerificationTxState.Cancelled -> { // is VerificationTxState.Cancelled -> {
renderCancel(txState.cancelCode) // renderCancel(txState.cancelCode)
} // }
is VerificationTxState.Done -> { // is VerificationTxState.Done -> {
//
} // }
else -> { // else -> {
// waiting // // waiting
bottomSheetVerificationWaitingItem { // bottomSheetVerificationWaitingItem {
id("waiting") // id("waiting")
title(host.stringProvider.getString(R.string.please_wait)) // title(host.stringProvider.getString(R.string.please_wait))
} // }
} // }
} // }
} // }
} // }
} // }
} // }
//
private fun renderCancel(cancelCode: CancelCode) { // private fun renderCancel(cancelCode: CancelCode) {
val host = this // val host = this
when (cancelCode) { // when (cancelCode) {
CancelCode.QrCodeInvalid -> { // CancelCode.QrCodeInvalid -> {
// TODO // // TODO
} // }
CancelCode.MismatchedUser, // CancelCode.MismatchedUser,
CancelCode.MismatchedSas, // CancelCode.MismatchedSas,
CancelCode.MismatchedCommitment, // CancelCode.MismatchedCommitment,
CancelCode.MismatchedKeys -> { // CancelCode.MismatchedKeys -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure).toEpoxyCharSequence())
} // }
//
bottomSheetVerificationBigImageItem { // bottomSheetVerificationBigImageItem {
id("image") // id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Warning) // roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Warning)
} // }
//
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("warning_notice") // id("warning_notice")
notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised)).toEpoxyCharSequence()) // notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised)).toEpoxyCharSequence())
} // }
} // }
else -> { // else -> {
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice_cancelled") // id("notice_cancelled")
notice(host.stringProvider.getString(R.string.verify_cancelled_notice).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verify_cancelled_notice).toEpoxyCharSequence())
} // }
} // }
} // }
} // }
//
private fun buildEmojiItem(emoji: List<EmojiRepresentation>) { // private fun buildEmojiItem(emoji: List<EmojiRepresentation>) {
val host = this // val host = this
bottomSheetVerificationNoticeItem { // bottomSheetVerificationNoticeItem {
id("notice") // id("notice")
notice(host.stringProvider.getString(R.string.verification_emoji_notice).toEpoxyCharSequence()) // notice(host.stringProvider.getString(R.string.verification_emoji_notice).toEpoxyCharSequence())
} // }
//
bottomSheetVerificationEmojisItem { // bottomSheetVerificationEmojisItem {
id("emojis") // id("emojis")
emojiRepresentation0(emoji[0]) // emojiRepresentation0(emoji[0])
emojiRepresentation1(emoji[1]) // emojiRepresentation1(emoji[1])
emojiRepresentation2(emoji[2]) // emojiRepresentation2(emoji[2])
emojiRepresentation3(emoji[3]) // emojiRepresentation3(emoji[3])
emojiRepresentation4(emoji[4]) // emojiRepresentation4(emoji[4])
emojiRepresentation5(emoji[5]) // emojiRepresentation5(emoji[5])
emojiRepresentation6(emoji[6]) // emojiRepresentation6(emoji[6])
} // }
//
buildSasCodeActions() // buildSasCodeActions()
} // }
//
private fun buildSasCodeActions() { // private fun buildSasCodeActions() {
val host = this // val host = this
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep0") // id("sep0")
} // }
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("ko") // id("ko")
title(host.stringProvider.getString(R.string.verification_sas_do_not_match)) // title(host.stringProvider.getString(R.string.verification_sas_do_not_match))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
iconRes(R.drawable.ic_check_off) // iconRes(R.drawable.ic_check_off)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
// listener { host.listener?.onDoNotMatchButtonTapped() } // // listener { host.listener?.onDoNotMatchButtonTapped() }
} // }
bottomSheetDividerItem { // bottomSheetDividerItem {
id("sep1") // id("sep1")
} // }
bottomSheetVerificationActionItem { // bottomSheetVerificationActionItem {
id("ok") // id("ok")
title(host.stringProvider.getString(R.string.verification_sas_match)) // title(host.stringProvider.getString(R.string.verification_sas_match))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_check_on) // iconRes(R.drawable.ic_check_on)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary)) // iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
// listener { host.listener?.onMatchButtonTapped() } // // listener { host.listener?.onMatchButtonTapped() }
} // }
} // }
} // }

View File

@ -1,36 +1,36 @@
/* // /*
* Copyright (c) 2022 New Vector Ltd // * Copyright (c) 2022 New Vector Ltd
* // *
* Licensed under the Apache License, Version 2.0 (the "License"); // * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. // * you may not use this file except in compliance with the License.
* You may obtain a copy of the License at // * You may obtain a copy of the License at
* // *
* http://www.apache.org/licenses/LICENSE-2.0 // * http://www.apache.org/licenses/LICENSE-2.0
* // *
* Unless required by applicable law or agreed to in writing, software // * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, // * distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and // * See the License for the specific language governing permissions and
* limitations under the License. // * limitations under the License.
*/ // */
//
package im.vector.app.features.crypto.verification.transaction // package im.vector.app.features.crypto.verification.transaction
//
import android.view.LayoutInflater // import android.view.LayoutInflater
import android.view.ViewGroup // import android.view.ViewGroup
import com.airbnb.mvrx.parentFragmentViewModel // import com.airbnb.mvrx.parentFragmentViewModel
import dagger.hilt.android.AndroidEntryPoint // import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.core.platform.VectorBaseFragment // import im.vector.app.core.platform.VectorBaseFragment
import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding // import im.vector.app.databinding.BottomSheetVerificationChildFragmentBinding
import im.vector.app.features.crypto.verification.user.UserVerificationViewModel // import im.vector.app.features.crypto.verification.user.UserVerificationViewModel
//
@AndroidEntryPoint // @AndroidEntryPoint
class VerificationTransactionFragment : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>() { // class VerificationTransactionFragment : VectorBaseFragment<BottomSheetVerificationChildFragmentBinding>() {
//
private val viewModel by parentFragmentViewModel(UserVerificationViewModel::class) // private val viewModel by parentFragmentViewModel(UserVerificationViewModel::class)
//
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding { // override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationChildFragmentBinding {
return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false) // return BottomSheetVerificationChildFragmentBinding.inflate(inflater, container, false)
} // }
//
} // }

View File

@ -22,10 +22,6 @@ import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.fragmentViewModel import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState import com.airbnb.mvrx.withState
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -34,15 +30,11 @@ import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.toMvRxBundle import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetVerificationBinding import im.vector.app.databinding.BottomSheetVerificationBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewEvents import im.vector.app.features.crypto.verification.VerificationBottomSheetViewEvents
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewModel
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import javax.inject.Inject import javax.inject.Inject
import kotlin.reflect.KClass import kotlin.reflect.KClass

View File

@ -18,7 +18,6 @@ package im.vector.app.features.crypto.verification.user
import androidx.core.text.toSpannable import androidx.core.text.toSpannable
import com.airbnb.epoxy.EpoxyController import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail 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
@ -43,8 +42,9 @@ import org.matrix.android.sdk.api.session.crypto.verification.CancelCode
import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState import org.matrix.android.sdk.api.session.crypto.verification.EVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.QRCodeVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.util.MatrixItem
import timber.log.Timber import timber.log.Timber
import javax.inject.Inject import javax.inject.Inject
@ -63,6 +63,8 @@ class UserVerificationController @Inject constructor(
fun onMatchButtonTapped() fun onMatchButtonTapped()
fun openCamera() fun openCamera()
fun doVerifyBySas() fun doVerifyBySas()
fun onUserDeniesQrCodeScanned()
fun onUserConfirmsQrCodeScanned()
} }
var listener: InteractionListener? = null var listener: InteractionListener? = null
@ -169,7 +171,7 @@ class UserVerificationController @Inject constructor(
} }
} }
is Fail -> { is Fail -> {
//TODO // TODO
} }
} }
} }
@ -257,14 +259,98 @@ class UserVerificationController @Inject constructor(
} }
private fun renderTransaction(state: UserVerificationViewState, transaction: VerificationTransactionData) { private fun renderTransaction(state: UserVerificationViewState, transaction: VerificationTransactionData) {
when (transaction) {
is VerificationTransactionData.QrTransactionData -> {
renderQrTransaction(transaction, state.otherUserMxItem)
}
is VerificationTransactionData.SasTransactionData -> {
renderSasTransaction(transaction)
}
}
}
private fun renderQrTransaction(transaction: VerificationTransactionData.QrTransactionData, otherUserItem: MatrixItem) {
val host = this val host = this
if (transaction.method == VerificationMethod.SAS) {
when (val txState = transaction.state) { when (val txState = transaction.state) {
VerificationTxState.SasShortCodeReady -> { QRCodeVerificationState.Reciprocated -> {
// we are waiting for confirmation from the other side
bottomSheetVerificationNoticeItem {
id("notice")
apply {
notice(host.stringProvider.getString(R.string.qr_code_scanned_verif_waiting_notice).toEpoxyCharSequence())
}
}
bottomSheetVerificationBigImageItem {
id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
}
bottomSheetVerificationWaitingItem {
id("waiting")
title(host.stringProvider.getString(R.string.qr_code_scanned_verif_waiting, otherUserItem.getBestName()))
}
}
QRCodeVerificationState.WaitingForScanConfirmation -> {
// we need to confirm that the other party actual scanned us
bottomSheetVerificationNoticeItem {
id("notice")
apply {
val name = otherUserItem.getBestName()
notice(host.stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name).toEpoxyCharSequence())
}
}
bottomSheetVerificationBigImageItem {
id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
}
bottomSheetDividerItem {
id("sep0")
}
bottomSheetVerificationActionItem {
id("deny")
title(host.stringProvider.getString(R.string.qr_code_scanned_by_other_no))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
iconRes(R.drawable.ic_check_off)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
listener { host.listener?.onUserDeniesQrCodeScanned() }
}
bottomSheetDividerItem {
id("sep1")
}
bottomSheetVerificationActionItem {
id("confirm")
title(host.stringProvider.getString(R.string.qr_code_scanned_by_other_yes))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_check_on)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener { host.listener?.onUserConfirmsQrCodeScanned() }
}
}
QRCodeVerificationState.WaitingForOtherDone,
QRCodeVerificationState.Done -> {
// Done
}
QRCodeVerificationState.Cancelled -> {
// Done
// renderCancel(transaction.)
}
}
}
private fun renderSasTransaction(transaction: VerificationTransactionData.SasTransactionData) {
val host = this
when (val txState = transaction.state) {
SasTransactionState.SasShortCodeReady -> {
buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty()) buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty())
} }
is VerificationTxState.SasMacReceived -> { is SasTransactionState.SasMacReceived -> {
if(!txState.codeConfirmed) { if (!txState.codeConfirmed) {
buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty()) buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty())
} else { } else {
// waiting // waiting
@ -274,8 +360,8 @@ class UserVerificationController @Inject constructor(
} }
} }
} }
is VerificationTxState.Cancelled, is SasTransactionState.Cancelled,
is VerificationTxState.Done -> { is SasTransactionState.Done -> {
// should show request status // should show request status
} }
else -> { else -> {
@ -286,9 +372,6 @@ class UserVerificationController @Inject constructor(
} }
} }
} }
} else {
// TODO (QR CODe
}
} }
private fun renderCancel(cancelCode: CancelCode) { private fun renderCancel(cancelCode: CancelCode) {

View File

@ -140,4 +140,12 @@ class UserVerificationFragment : VectorBaseFragment<BottomSheetVerificationChild
override fun doVerifyBySas() { override fun doVerifyBySas() {
viewModel.handle(VerificationAction.StartSASVerification) viewModel.handle(VerificationAction.StartSASVerification)
} }
override fun onUserDeniesQrCodeScanned() {
viewModel.handle(VerificationAction.OtherUserDidNotScanned)
}
override fun onUserConfirmsQrCodeScanned() {
viewModel.handle(VerificationAction.OtherUserScannedSuccessfully)
}
} }

View File

@ -35,7 +35,6 @@ import im.vector.app.features.crypto.verification.SupportedVerificationMethodsPr
import im.vector.app.features.crypto.verification.VerificationAction import im.vector.app.features.crypto.verification.VerificationAction
import im.vector.app.features.crypto.verification.VerificationBottomSheetViewEvents import im.vector.app.features.crypto.verification.VerificationBottomSheetViewEvents
import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.forEach
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@ -44,14 +43,13 @@ import org.matrix.android.sdk.api.extensions.orFalse
import org.matrix.android.sdk.api.extensions.tryOrNull import org.matrix.android.sdk.api.extensions.tryOrNull
import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.Session
import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation import org.matrix.android.sdk.api.session.crypto.verification.EmojiRepresentation
import org.matrix.android.sdk.api.session.crypto.verification.IVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest import org.matrix.android.sdk.api.session.crypto.verification.PendingVerificationRequest
import org.matrix.android.sdk.api.session.crypto.verification.QRCodeVerificationState
import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.QrCodeVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.SasTransactionState
import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.SasVerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod import org.matrix.android.sdk.api.session.crypto.verification.VerificationMethod
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.crypto.verification.getRequest import org.matrix.android.sdk.api.session.crypto.verification.getRequest
import org.matrix.android.sdk.api.session.crypto.verification.getTransaction import org.matrix.android.sdk.api.session.crypto.verification.getTransaction
import org.matrix.android.sdk.api.session.getUser import org.matrix.android.sdk.api.session.getUser
@ -82,25 +80,52 @@ data class UserVerificationViewState(
} }
// We need immutable objects to use properly in MvrxState // We need immutable objects to use properly in MvrxState
data class VerificationTransactionData( sealed class VerificationTransactionData(
val transactionId: String, open val transactionId: String,
val state: VerificationTxState, open val otherUserId: String,
val method: VerificationMethod, ) {
val otherUserId: String,
data class SasTransactionData(
override val transactionId: String,
val state: SasTransactionState,
override val otherUserId: String,
val otherDeviceId: String?, val otherDeviceId: String?,
val isIncoming: Boolean, val isIncoming: Boolean,
val emojiCodeRepresentation: List<EmojiRepresentation>? val emojiCodeRepresentation: List<EmojiRepresentation>?
) ) : VerificationTransactionData(transactionId, otherUserId)
fun VerificationTransaction.toDataClass() : VerificationTransactionData {
return VerificationTransactionData( data class QrTransactionData(
override val transactionId: String,
val state: QRCodeVerificationState,
override val otherUserId: String,
val otherDeviceId: String?,
val isIncoming: Boolean,
) : VerificationTransactionData(transactionId, otherUserId)
}
private fun VerificationTransaction.toDataClass(): VerificationTransactionData? {
return when (this) {
is SasVerificationTransaction -> {
VerificationTransactionData.SasTransactionData(
transactionId = this.transactionId, transactionId = this.transactionId,
state = this.state, state = this.state(),
method = this.method,
otherUserId = this.otherUserId, otherUserId = this.otherUserId,
otherDeviceId = this.otherUserId, otherDeviceId = this.otherDeviceId,
isIncoming = this.isIncoming, isIncoming = this.isIncoming,
emojiCodeRepresentation = (this as? SasVerificationTransaction)?.getEmojiCodeRepresentation() emojiCodeRepresentation = this.getEmojiCodeRepresentation()
) )
}
is QrCodeVerificationTransaction -> {
VerificationTransactionData.QrTransactionData(
transactionId = this.transactionId,
state = this.state(),
otherUserId = this.otherUserId,
otherDeviceId = this.otherDeviceId,
isIncoming = this.isIncoming,
)
}
else -> null
}
} }
class UserVerificationViewModel @AssistedInject constructor( class UserVerificationViewModel @AssistedInject constructor(
@ -127,8 +152,8 @@ class UserVerificationViewModel @AssistedInject constructor(
session.cryptoService().verificationService() session.cryptoService().verificationService()
.requestEventFlow() .requestEventFlow()
.filter { .filter {
it.transactionId == currentTransactionId it.transactionId == currentTransactionId ||
|| currentTransactionId == null && initialState.otherUserId == it.getRequest()?.otherUserId currentTransactionId == null && initialState.otherUserId == it.getRequest()?.otherUserId
} }
.onEach { .onEach {
Timber.w("VALR update event ${it.getRequest()} ") Timber.w("VALR update event ${it.getRequest()} ")
@ -142,11 +167,20 @@ class UserVerificationViewModel @AssistedInject constructor(
} }
it.getTransaction()?.let { it.getTransaction()?.let {
Timber.w("VALR state updated transaction to $it") Timber.w("VALR state updated transaction to $it")
val dClass = it.toDataClass()
if (dClass != null) {
setState { setState {
copy( copy(
startedTransaction = Success(it.toDataClass()), startedTransaction = Success(dClass),
) )
} }
} else {
setState {
copy(
startedTransaction = Fail(IllegalArgumentException("Unsupported Transaction")),
)
}
}
} }
} }
.launchIn(viewModelScope) .launchIn(viewModelScope)
@ -229,8 +263,30 @@ class UserVerificationViewModel @AssistedInject constructor(
is VerificationAction.GotResultFromSsss -> { is VerificationAction.GotResultFromSsss -> {
// not applicable, only for self verification // not applicable, only for self verification
} }
VerificationAction.OtherUserDidNotScanned -> TODO() VerificationAction.OtherUserDidNotScanned -> {
VerificationAction.OtherUserScannedSuccessfully -> TODO() withState { state ->
state.startedTransaction.invoke()?.let {
viewModelScope.launch {
val tx = session.cryptoService().verificationService()
.getExistingTransaction(it.otherUserId, it.transactionId)
as? QrCodeVerificationTransaction
tx?.otherUserDidNotScannedMyQrCode()
}
}
}
}
VerificationAction.OtherUserScannedSuccessfully -> {
withState { state ->
state.startedTransaction.invoke()?.let {
viewModelScope.launch {
val tx = session.cryptoService().verificationService()
.getExistingTransaction(it.otherUserId, it.transactionId)
as? QrCodeVerificationTransaction
tx?.otherUserScannedMyQrCode()
}
}
}
}
VerificationAction.ReadyPendingVerification -> { VerificationAction.ReadyPendingVerification -> {
withState { state -> withState { state ->
state.pendingRequest.invoke()?.let { state.pendingRequest.invoke()?.let {
@ -244,13 +300,34 @@ class UserVerificationViewModel @AssistedInject constructor(
} }
} }
} }
is VerificationAction.RemoteQrCodeScanned -> TODO() is VerificationAction.RemoteQrCodeScanned -> {
setState {
copy(startedTransaction = Loading())
}
withState { state ->
val request = state.pendingRequest.invoke() ?: return@withState
viewModelScope.launch {
try {
session.cryptoService().verificationService()
.reciprocateQRVerification(
request.otherUserId,
request.transactionId,
action.scannedData
)
} catch (failure: Throwable) {
Timber.w(failure, "Failed to reciprocated")
setState {
copy(startedTransaction = Fail(failure))
}
}
}
}
}
is VerificationAction.RequestVerificationByDM -> { is VerificationAction.RequestVerificationByDM -> {
setState { setState {
copy(pendingRequest = Loading()) copy(pendingRequest = Loading())
} }
viewModelScope.launch { viewModelScope.launch {
// TODO if self verif we should do via DM // TODO if self verif we should do via DM
val roomId = session.roomService().getExistingDirectRoomWithUser(initialState.otherUserId) val roomId = session.roomService().getExistingDirectRoomWithUser(initialState.otherUserId)
?: session.roomService().createDirectRoom(initialState.otherUserId) ?: session.roomService().createDirectRoom(initialState.otherUserId)
@ -266,14 +343,12 @@ class UserVerificationViewModel @AssistedInject constructor(
Timber.w("VALR started request is $request") Timber.w("VALR started request is $request")
setState { setState {
copy( copy(
pendingRequest = Success(request), pendingRequest = Success(request),
transactionId = request.transactionId transactionId = request.transactionId
) )
} }
} }
} }
is VerificationAction.SASDoNotMatchAction -> { is VerificationAction.SASDoNotMatchAction -> {

View File

@ -265,7 +265,7 @@ class HomeActivity :
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush() HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup() HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup()
is HomeActivityViewEvents.ForceVerification -> { is HomeActivityViewEvents.ForceVerification -> {
//TODO // TODO
// if (it.sendRequest) { // if (it.sendRequest) {
navigator.requestSelfSessionVerification(this) navigator.requestSelfSessionVerification(this)
// } else { // } else {

View File

@ -124,7 +124,6 @@ import im.vector.app.features.call.conference.ConferenceEventObserver
import im.vector.app.features.call.conference.JitsiCallViewModel import im.vector.app.features.call.conference.JitsiCallViewModel
import im.vector.app.features.call.webrtc.WebRtcCallManager import im.vector.app.features.call.webrtc.WebRtcCallManager
import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity import im.vector.app.features.crypto.keysbackup.restore.KeysBackupRestoreActivity
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.crypto.verification.user.UserVerificationBottomSheet import im.vector.app.features.crypto.verification.user.UserVerificationBottomSheet
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.home.room.detail.arguments.TimelineArgs import im.vector.app.features.home.room.detail.arguments.TimelineArgs

View File

@ -52,7 +52,6 @@ import im.vector.app.features.crypto.keysbackup.setup.KeysBackupSetupActivity
import im.vector.app.features.crypto.recover.BootstrapBottomSheet import im.vector.app.features.crypto.recover.BootstrapBottomSheet
import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider import im.vector.app.features.crypto.verification.SupportedVerificationMethodsProvider
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.devtools.RoomDevToolActivity import im.vector.app.features.devtools.RoomDevToolActivity
import im.vector.app.features.home.room.detail.RoomDetailActivity import im.vector.app.features.home.room.detail.RoomDetailActivity
import im.vector.app.features.home.room.detail.arguments.TimelineArgs import im.vector.app.features.home.room.detail.arguments.TimelineArgs

View File

@ -50,7 +50,6 @@ import im.vector.app.databinding.DialogShareQrCodeBinding
import im.vector.app.databinding.FragmentMatrixProfileBinding import im.vector.app.databinding.FragmentMatrixProfileBinding
import im.vector.app.databinding.ViewStubRoomMemberProfileHeaderBinding import im.vector.app.databinding.ViewStubRoomMemberProfileHeaderBinding
import im.vector.app.features.analytics.plan.MobileScreen import im.vector.app.features.analytics.plan.MobileScreen
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.crypto.verification.user.UserVerificationBottomSheet import im.vector.app.features.crypto.verification.user.UserVerificationBottomSheet
import im.vector.app.features.displayname.getBestName import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer import im.vector.app.features.home.AvatarRenderer

View File

@ -31,7 +31,6 @@ import im.vector.app.R
import im.vector.app.core.extensions.commitTransaction import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetWithFragmentsBinding import im.vector.app.databinding.BottomSheetWithFragmentsBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import kotlinx.parcelize.Parcelize import kotlinx.parcelize.Parcelize
import kotlin.reflect.KClass import kotlin.reflect.KClass
@ -50,11 +49,12 @@ class DeviceListBottomSheet :
viewModel.observeViewEvents { viewModel.observeViewEvents {
when (it) { when (it) {
is DeviceListBottomSheetViewEvents.Verify -> { is DeviceListBottomSheetViewEvents.Verify -> {
VerificationBottomSheet.withArgs( // TODO selfverif
// roomId = null, // VerificationBottomSheet.withArgs(
otherUserId = it.userId, // // roomId = null,
transactionId = it.txID // otherUserId = it.userId,
).show(requireActivity().supportFragmentManager, "REQPOP") // transactionId = it.txID
// ).show(requireActivity().supportFragmentManager, "REQPOP")
} }
} }
} }

View File

@ -60,7 +60,6 @@ import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth import org.matrix.android.sdk.api.session.uia.DefaultBaseAuth
import org.matrix.android.sdk.flow.flow import org.matrix.android.sdk.flow.flow
import timber.log.Timber import timber.log.Timber
@ -126,7 +125,7 @@ class DevicesViewModel @AssistedInject constructor(
session.cryptoService().verificationService().requestEventFlow() session.cryptoService().verificationService().requestEventFlow()
.onEach { .onEach {
when(it) { when (it) {
is VerificationEvent.RequestUpdated -> { is VerificationEvent.RequestUpdated -> {
if (it.request.isFinished) { if (it.request.isFinished) {
queryRefreshDevicesList() queryRefreshDevicesList()
@ -241,7 +240,7 @@ class DevicesViewModel @AssistedInject constructor(
} }
override fun transactionUpdated(tx: VerificationTransaction) { override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.state == VerificationTxState.Verified) { if (tx.isSuccessful()) {
queryRefreshDevicesList() queryRefreshDevicesList()
} }
} }

View File

@ -39,7 +39,6 @@ import im.vector.app.databinding.DialogBaseEditTextBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.auth.ReAuthActivity import im.vector.app.features.auth.ReAuthActivity
import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import org.matrix.android.sdk.api.auth.data.LoginFlowTypes import org.matrix.android.sdk.api.auth.data.LoginFlowTypes
import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo import org.matrix.android.sdk.api.session.crypto.model.DeviceInfo
import javax.inject.Inject import javax.inject.Inject
@ -78,11 +77,12 @@ class VectorSettingsDevicesFragment :
is DevicesViewEvents.RequestReAuth -> askForReAuthentication(it) is DevicesViewEvents.RequestReAuth -> askForReAuthentication(it)
is DevicesViewEvents.PromptRenameDevice -> displayDeviceRenameDialog(it.deviceInfo) is DevicesViewEvents.PromptRenameDevice -> displayDeviceRenameDialog(it.deviceInfo)
is DevicesViewEvents.ShowVerifyDevice -> { is DevicesViewEvents.ShowVerifyDevice -> {
VerificationBottomSheet.withArgs( // TODO selfverif
// roomId = null, // VerificationBottomSheet.withArgs(
otherUserId = it.userId, // // roomId = null,
transactionId = it.transactionId ?: "" // otherUserId = it.userId,
).show(childFragmentManager, "REQPOP") // transactionId = it.transactionId ?: ""
// ).show(childFragmentManager, "REQPOP")
} }
is DevicesViewEvents.SelfVerification -> { is DevicesViewEvents.SelfVerification -> {
navigator.requestSelfSessionVerification(requireActivity()) navigator.requestSelfSessionVerification(requireActivity())

View File

@ -25,11 +25,9 @@ import im.vector.app.core.utils.PublishDataSource
import im.vector.lib.core.utils.flow.throttleFirst import im.vector.lib.core.utils.flow.throttleFirst
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent import org.matrix.android.sdk.api.session.crypto.verification.VerificationEvent
import org.matrix.android.sdk.api.session.crypto.verification.VerificationService import org.matrix.android.sdk.api.session.crypto.verification.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTxState
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
abstract class VectorSessionsListViewModel<S : MavericksState, VA : VectorViewModelAction, VE : VectorViewEvents>( abstract class VectorSessionsListViewModel<S : MavericksState, VA : VectorViewModelAction, VE : VectorViewEvents>(
@ -85,7 +83,7 @@ abstract class VectorSessionsListViewModel<S : MavericksState, VA : VectorViewMo
} }
override fun transactionUpdated(tx: VerificationTransaction) { override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.state == VerificationTxState.Verified) { if (tx.isSuccessful()) {
refreshDeviceList() refreshDeviceList()
} }
} }

View File

@ -36,7 +36,6 @@ import im.vector.app.core.resources.DrawableProvider
import im.vector.app.core.resources.StringProvider import im.vector.app.core.resources.StringProvider
import im.vector.app.databinding.FragmentSettingsDevicesBinding import im.vector.app.databinding.FragmentSettingsDevicesBinding
import im.vector.app.features.crypto.recover.SetupMode import im.vector.app.features.crypto.recover.SetupMode
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType import im.vector.app.features.settings.devices.v2.filter.DeviceManagerFilterType
import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER import im.vector.app.features.settings.devices.v2.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER
import im.vector.app.features.settings.devices.v2.list.OtherSessionsView import im.vector.app.features.settings.devices.v2.list.OtherSessionsView
@ -98,11 +97,12 @@ class VectorSettingsDevicesFragment :
is DevicesViewEvent.RequestReAuth -> Unit // TODO. Next PR is DevicesViewEvent.RequestReAuth -> Unit // TODO. Next PR
is DevicesViewEvent.PromptRenameDevice -> Unit // TODO. Next PR is DevicesViewEvent.PromptRenameDevice -> Unit // TODO. Next PR
is DevicesViewEvent.ShowVerifyDevice -> { is DevicesViewEvent.ShowVerifyDevice -> {
VerificationBottomSheet.withArgs( // TODO selfverif
// roomId = null, // VerificationBottomSheet.withArgs(
otherUserId = it.userId, // // roomId = null,
transactionId = it.transactionId ?:"" // otherUserId = it.userId,
).show(childFragmentManager, "REQPOP") // transactionId = it.transactionId ?:""
// ).show(childFragmentManager, "REQPOP")
} }
is DevicesViewEvent.SelfVerification -> { is DevicesViewEvent.SelfVerification -> {
navigator.requestSelfSessionVerification(requireActivity()) navigator.requestSelfSessionVerification(requireActivity())