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.SasVerificationTransaction
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.toModel
import org.matrix.android.sdk.api.session.getRoom

View File

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

View File

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

View File

@ -16,41 +16,67 @@
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.QRCodeVerificationState
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.VerificationTxState
import org.matrix.android.sdk.internal.crypto.verification.qrcode.QrCodeData
import org.matrix.android.sdk.internal.crypto.verification.qrcode.toEncodedString
class KotlinQRVerification(
override val qrCodeText: String?,
override val state: VerificationTxState,
internal class KotlinQRVerification(
private val channel: Channel<VerificationIntent>,
var qrCodeData: QrCodeData?,
override val method: VerificationMethod,
override val transactionId: String,
override val otherUserId: 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) {
TODO("Not yet implemented")
}
override fun state() = state
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() {
TODO("Not yet implemented")
val deferred = CompletableDeferred<Unit>()
channel.send(
VerificationIntent.ActionConfirmCodeWasScanned(otherUserId, transactionId, deferred)
)
deferred.await()
}
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() {
TODO("Not yet implemented")
cancel(CancelCode.User)
}
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 {
TODO("Not yet implemented")
}
override fun isToDeviceTransport() = isToDevice
}

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.EmojiRepresentation
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.Companion.KEY_AGREEMENT_V1
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_LONGKDF
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.room.model.message.MessageVerificationAcceptContent
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 java.util.Locale
internal class SasV1Transaction(
internal class KotlinSasTransaction(
private val channel: Channel<VerificationIntent>,
override var state: VerificationTxState,
override val transactionId: String,
override val otherUserId: String,
private val myUserId: String,
@ -63,6 +62,7 @@ internal class SasV1Transaction(
override val isIncoming: Boolean,
val startReq: ValidVerificationInfoStart.SasVerificationInfoStart? = null,
val isToDevice: Boolean,
var state: SasTransactionState
) : SasVerificationTransaction {
override val method: VerificationMethod
@ -207,6 +207,8 @@ internal class SasV1Transaction(
var myMac: ValidVerificationInfoMac? = null
var theirMac: ValidVerificationInfoMac? = null
override fun state() = this.state
override fun supportsEmoji(): Boolean {
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.session.crypto.crosssigning.CrossSigningService
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.SendToDeviceObject
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.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.ValidVerificationInfoReady
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.VerificationMethod
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.events.model.Content
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.MessageVerificationStartContent
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.model.rest.KeyVerificationCancel
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 sendToDeviceTask: SendToDeviceTask,
private val setDeviceVerificationAction: SetDeviceVerificationAction,
private val crossSigningService: dagger.Lazy<CrossSigningService>
private val crossSigningService: dagger.Lazy<CrossSigningService>,
private val secretShareManager: SecretShareManager,
) {
@AssistedFactory
@ -121,6 +127,34 @@ internal class VerificationActor @AssistedInject constructor(
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(
otherUserId: String,
requestId: String,
@ -202,7 +236,13 @@ internal class VerificationActor @AssistedInject constructor(
handleSasStart(msg)
}
is VerificationIntent.ActionReciprocateQrVerification -> {
handleReciprocateQR(msg)
handleActionReciprocateQR(msg)
}
is VerificationIntent.ActionConfirmCodeWasScanned -> {
withMatchingRequest(msg.otherUserId, msg.requestId) {
handleActionQRScanConfirmed(it)
}
msg.deferred.complete(Unit)
}
is VerificationIntent.OnStartReceived -> {
onStartReceived(msg)
@ -277,14 +317,15 @@ internal class VerificationActor @AssistedInject constructor(
request.state = EVerificationState.Cancelled
val cancelCode = safeValueOf(msg.validCancel.code)
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) {
existingTx.state = VerificationTxState.Cancelled(cancelCode, false)
existingTx.state = SasTransactionState.Cancelled(cancelCode, false)
txMap[msg.fromUser]?.remove(msg.validCancel.transactionId)
eventFlow.emit(VerificationEvent.TransactionUpdated(existingTx))
}
eventFlow.emit(VerificationEvent.RequestUpdated(request.toPendingVerificationRequest()))
}
}
is VerificationIntent.OnReadyByAnotherOfMySessionReceived -> {
@ -358,6 +399,27 @@ internal class VerificationActor @AssistedInject constructor(
}
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(
@ -372,7 +434,7 @@ internal class VerificationActor @AssistedInject constructor(
// and the other m.key.verification.start event is ignored.
// So let's check if I already send a start?
val requestId = msg.validVerificationInfoStart.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId)
val existing: KotlinSasTransaction? = getExistingTransaction(msg.fromUser, requestId)
if (existing != null) {
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] No existing Sas transaction for ${request.requestId}")
@ -380,23 +442,6 @@ internal class VerificationActor @AssistedInject constructor(
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
// 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.
@ -440,11 +485,28 @@ internal class VerificationActor @AssistedInject constructor(
cancelRequest(request, CancelCode.UserError)
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 commitment = hashUsingAgreedHashMethod(agreedHash, concat)
val accept = SasV1Transaction.sasAccept(
val accept = KotlinSasTransaction.sasAccept(
inRoom = request.roomId != null,
requestId = requestId,
keyAgreementProtocol = agreedProtocol,
@ -464,7 +526,6 @@ internal class VerificationActor @AssistedInject constructor(
}
sasTx.accepted = accept.asValidObject()
sasTx.state = VerificationTxState.SasAccepted
addTransaction(sasTx)
}
@ -472,13 +533,13 @@ internal class VerificationActor @AssistedInject constructor(
private suspend fun handleReceiveAccept(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnAcceptReceived) {
val requestId = msg.validAccept.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId)
val existing: KotlinSasTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also {
Timber.v("on accept received in room ${msg.viaRoom} for verification id:${requestId} in room ${matchingRequest.roomId}")
}
// Existing should be in
if (existing.state != VerificationTxState.SasStarted) {
if (existing.state != SasTransactionState.SasStarted) {
// it's a wrong state should 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
val pubKey = existing.getSAS().publicKey
val keyMessage = SasV1Transaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey)
val keyMessage = KotlinSasTransaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey)
try {
if (BuildConfig.LOG_PRIVATE_DATA) {
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}]: Sending my key $pubKey")
@ -515,7 +575,7 @@ internal class VerificationActor @AssistedInject constructor(
keyMessage,
)
} catch (failure: Throwable) {
existing.state = VerificationTxState.Cancelled(CancelCode.UserError, true)
existing.state = SasTransactionState.Cancelled(CancelCode.UserError, true)
matchingRequest.cancelCode = CancelCode.UserError
matchingRequest.state = EVerificationState.Cancelled
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
@ -523,7 +583,7 @@ internal class VerificationActor @AssistedInject constructor(
return
}
existing.accepted = accept
existing.state = VerificationTxState.SasKeySent
existing.state = SasTransactionState.SasKeySent
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"))
}
val existingTransaction = getExistingTransaction(msg.otherUserId, msg.requestId)
val existingTransaction = getExistingTransaction<VerificationTransaction>(msg.otherUserId, msg.requestId)
if (existingTransaction is SasVerificationTransaction) {
// there is already an existing transaction??
msg.deferred.completeExceptionally(IllegalStateException("Already started"))
return
}
val startMessage = SasV1Transaction.sasStart(
val startMessage = KotlinSasTransaction.sasStart(
inRoom = matchingRequest.roomId != null,
fromDevice = cryptoStore.getDeviceId(),
requestId = msg.requestId
@ -564,10 +624,10 @@ internal class VerificationActor @AssistedInject constructor(
)
// should check if already one (and cancel it)
val tx = SasV1Transaction(
val tx = KotlinSasTransaction(
channel = channel,
transactionId = msg.requestId,
state = VerificationTxState.SasStarted,
state = SasTransactionState.SasStarted,
otherUserId = msg.otherUserId,
myUserId = myUserId,
myTrustedMSK = cryptoStore.getMyCrossSigningInfo()
@ -589,16 +649,22 @@ internal class VerificationActor @AssistedInject constructor(
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
.flatMap { entry ->
entry.value.filter { it.requestId == msg.requestId }
}.firstOrNull()
?: 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"))
}
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"))
return
}
@ -607,21 +673,24 @@ internal class VerificationActor @AssistedInject constructor(
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??
if (existingTransaction != null) {
// cancel or replace??
Timber.tag(loggerTag.value)
.w("[${myUserId.take(8)}] There is already a started transaction for request ${msg.requestId}")
return
}
val myMasterKey = crossSigningService.get()
.getUserCrossSigningKeys(myUserId)?.masterKey()?.unpaddedBase64PublicKey
var canTrustOtherUserMasterKey = false
// Check the other device view of my MSK
val otherQrCodeData = msg.scannedData.toQrCodeData()
when (otherQrCodeData) {
null -> {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] Malformed QR code ${msg.requestId}")
msg.deferred.completeExceptionally(IllegalArgumentException("Malformed QrCode data"))
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.
// Let's check that it's correct
// If not -> Cancel
if (otherQrCodeData.otherUserMasterCrossSigningPublicKey != myMasterKey) {
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.otherUserMasterCrossSigningPublicKey}")
val whatOtherThinksMyMskIs = 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)
msg.deferred.complete(null)
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 -> {
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.
// Let's check that I see the same MSK
// If not -> Cancel
if (otherQrCodeData.userMasterCrossSigningPublicKey != myMasterKey) {
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
val whatOtherThinksOurMskIs = 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)
msg.deferred.complete(null)
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 -> {
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.
// Let's check that it's the good one
// If not -> Cancel
if (otherQrCodeData.userMasterCrossSigningPublicKey != myMasterKey) {
Timber.d("## Verification QR: Invalid other master key ${otherQrCodeData.userMasterCrossSigningPublicKey}")
val otherDeclaredDeviceKey = otherQrCodeData.deviceKey
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)
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>()
// Let's now check the other user/device key material
when (otherQrCodeData) {
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}")
val ownMasterKeyTrustedAsSeenByOther = otherQrCodeData.userMasterCrossSigningPublicKey
if (ownMasterKeyTrustedAsSeenByOther != 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
} 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
@ -746,6 +807,8 @@ internal class VerificationActor @AssistedInject constructor(
try {
sendToOther(matchingRequest, EventType.KEY_VERIFICATION_START, message)
} catch (failure: Throwable) {
Timber.tag(loggerTag.value)
.d("[${myUserId.take(8)}] Failed to send reciprocate message")
msg.deferred.completeExceptionally(failure)
return
}
@ -754,21 +817,45 @@ internal class VerificationActor @AssistedInject constructor(
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
val tx = KotlinQRVerification(
qrCodeText = msg.scannedData,
state = VerificationTxState.WaitingOtherReciprocateConfirm,
channel = this.channel,
state = QRCodeVerificationState.Reciprocated,
qrCodeData = msg.scannedData.toQrCodeData(),
method = VerificationMethod.QR_CODE_SCAN,
transactionId = msg.requestId,
otherUserId = msg.otherUserId,
otherDeviceId = matchingRequest.otherDeviceId(),
isIncoming = false
isIncoming = false,
isToDevice = matchingRequest.roomId == null
)
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) {
val requestId = msg.validKey.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId)
val existing: KotlinSasTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also {
Timber.tag(loggerTag.value)
.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
val isCorrectState = if (existing.isIncoming) {
existing.state == VerificationTxState.SasAccepted
existing.state == SasTransactionState.SasAccepted
} else {
existing.state == VerificationTxState.SasKeySent
existing.state == SasTransactionState.SasKeySent
}
if (!isCorrectState) {
@ -792,7 +879,7 @@ internal class VerificationActor @AssistedInject constructor(
if (existing.isIncoming) {
// ok i can now send my key and compute the sas code
val pubKey = existing.getSAS().publicKey
val keyMessage = SasV1Transaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey)
val keyMessage = KotlinSasTransaction.sasKeyMessage(matchingRequest.roomId != null, requestId, pubKey)
try {
sendToOther(
matchingRequest,
@ -804,7 +891,7 @@ internal class VerificationActor @AssistedInject constructor(
.v("[${myUserId.take(8)}]:i calculate SAS my key $pubKey their Key: $otherKey")
}
existing.calculateSASBytes(otherKey)
existing.state = VerificationTxState.SasShortCodeReady
existing.state = SasTransactionState.SasShortCodeReady
if (BuildConfig.LOG_PRIVATE_DATA) {
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}]:i CODE ${existing.getDecimalCodeRepresentation()}")
@ -813,7 +900,7 @@ internal class VerificationActor @AssistedInject constructor(
}
eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
} catch (failure: Throwable) {
existing.state = VerificationTxState.Cancelled(CancelCode.UserError, true)
existing.state = SasTransactionState.Cancelled(CancelCode.UserError, true)
matchingRequest.state = EVerificationState.Cancelled
matchingRequest.cancelCode = CancelCode.UserError
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")
}
existing.calculateSASBytes(otherKey)
existing.state = VerificationTxState.SasShortCodeReady
existing.state = SasTransactionState.SasShortCodeReady
eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
if (BuildConfig.LOG_PRIVATE_DATA) {
Timber.tag(loggerTag.value)
@ -864,21 +951,21 @@ internal class VerificationActor @AssistedInject constructor(
private suspend fun handleMacReceived(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnMacReceived) {
val requestId = msg.validMac.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId)
val existing: KotlinSasTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also {
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] on Mac for unknown transaction with id:$requestId")
}
when (existing.state) {
is VerificationTxState.SasMacSent -> {
is SasTransactionState.SasMacSent -> {
existing.theirMac = msg.validMac
finalizeSasTransaction(existing, msg.validMac, matchingRequest, existing.transactionId)
}
is VerificationTxState.SasShortCodeReady -> {
is SasTransactionState.SasShortCodeReady -> {
// I can start verify, store it
existing.theirMac = msg.validMac
existing.state = VerificationTxState.SasMacReceived(false)
existing.state = SasTransactionState.SasMacReceived(false)
eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
}
else -> {
@ -901,14 +988,14 @@ internal class VerificationActor @AssistedInject constructor(
msg.deferred.completeExceptionally(IllegalStateException("Request was cancelled"))
}
}
val existing = getExistingTransaction(transactionId)
val existing: KotlinSasTransaction = getExistingTransaction(transactionId)
?: return Unit.also {
msg.deferred.completeExceptionally(IllegalStateException("Unknown Transaction"))
}
val isCorrectState = when (val state = existing.state) {
is VerificationTxState.SasShortCodeReady -> true
is VerificationTxState.SasMacReceived -> !state.codeConfirmed
is SasTransactionState.SasShortCodeReady -> true
is SasTransactionState.SasMacReceived -> !state.codeConfirmed
else -> false
}
if (!isCorrectState) {
@ -927,25 +1014,122 @@ internal class VerificationActor @AssistedInject constructor(
private suspend fun handleDoneReceived(matchingRequest: KotlinVerificationRequest, msg: VerificationIntent.OnDoneReceived) {
val requestId = msg.transactionId
val existing = getExistingTransaction(msg.fromUser, requestId)
val existing: VerificationTransaction = getExistingTransaction(msg.fromUser, requestId)
?: return Unit.also {
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 isCorrectState = state is VerificationTxState.Done && !state.otherDone
val isCorrectState = state is SasTransactionState.Done && !state.otherDone
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?
matchingRequest.state = EVerificationState.Done
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
// updatePendingRequest(
// matchingRequest.copy(
// isSuccessful = true
// )
// )
} else {
// 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"))
}
if (matchingRequest.state != EVerificationState.WeStarted
&& matchingRequest.state != EVerificationState.Started) {
if (matchingRequest.state != EVerificationState.WeStarted &&
matchingRequest.state != EVerificationState.Started) {
return Unit.also {
msg.deferred.completeExceptionally(IllegalStateException("Can't accept code in state: ${matchingRequest.state}"))
}
}
val existing = getExistingTransaction(transactionId)
val existing: KotlinSasTransaction = getExistingTransaction(transactionId)
?: return Unit.also {
msg.deferred.completeExceptionally(IllegalStateException("Unknown Transaction"))
}
val isCorrectState = when (val state = existing.state) {
is VerificationTxState.SasShortCodeReady -> true
is VerificationTxState.SasMacReceived -> !state.codeConfirmed
is SasTransactionState.SasShortCodeReady -> true
is SasTransactionState.SasMacReceived -> !state.codeConfirmed
else -> false
}
if (!isCorrectState) {
@ -981,7 +1165,7 @@ internal class VerificationActor @AssistedInject constructor(
val macInfo = existing.computeMyMac()
val macMsg = SasV1Transaction.sasMacMessage(matchingRequest.roomId != null, transactionId, macInfo)
val macMsg = KotlinSasTransaction.sasMacMessage(matchingRequest.roomId != null, transactionId, macInfo)
try {
sendToOther(matchingRequest, EventType.KEY_VERIFICATION_MAC, macMsg)
} catch (failure: Throwable) {
@ -995,7 +1179,7 @@ internal class VerificationActor @AssistedInject constructor(
if (theirMac != null) {
finalizeSasTransaction(existing, theirMac, matchingRequest, transactionId)
} else {
existing.state = VerificationTxState.SasMacSent
existing.state = SasTransactionState.SasMacSent
eventFlow.emit(VerificationEvent.TransactionUpdated(existing))
}
@ -1003,7 +1187,7 @@ internal class VerificationActor @AssistedInject constructor(
}
private suspend fun finalizeSasTransaction(
existing: SasV1Transaction,
existing: KotlinSasTransaction,
theirMac: ValidVerificationInfoMac,
matchingRequest: KotlinVerificationRequest,
transactionId: String
@ -1017,7 +1201,7 @@ internal class VerificationActor @AssistedInject constructor(
Timber.tag(loggerTag.value)
.v("[${myUserId.take(8)}] verify macs result $result id:$transactionId")
when (result) {
is SasV1Transaction.MacVerificationResult.Success -> {
is KotlinSasTransaction.MacVerificationResult.Success -> {
// mark the devices as locally trusted
result.verifiedDeviceId.forEach { deviceId ->
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))
pastTransactions.getOrPut(transactionId) { mutableMapOf() }[transactionId] = existing
txMap[matchingRequest.otherUserId]?.remove(transactionId)
matchingRequest.state = EVerificationState.WaitingForDone
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
}
SasV1Transaction.MacVerificationResult.MismatchKeys,
SasV1Transaction.MacVerificationResult.MismatchMacCrossSigning,
is SasV1Transaction.MacVerificationResult.MismatchMacDevice,
SasV1Transaction.MacVerificationResult.NoDevicesVerified -> {
KotlinSasTransaction.MacVerificationResult.MismatchKeys,
KotlinSasTransaction.MacVerificationResult.MismatchMacCrossSigning,
is KotlinSasTransaction.MacVerificationResult.MismatchMacDevice,
KotlinSasTransaction.MacVerificationResult.NoDevicesVerified -> {
cancelRequest(matchingRequest, CancelCode.MismatchedKeys)
}
}
@ -1126,7 +1310,7 @@ internal class VerificationActor @AssistedInject constructor(
commonMethods
)
val message = SasV1Transaction.sasReady(
val message = KotlinSasTransaction.sasReady(
inRoom = existing.roomId != null,
requestId = msg.transactionId,
methods = commonMethods,
@ -1533,11 +1717,17 @@ internal class VerificationActor @AssistedInject constructor(
eventFlow.emit(VerificationEvent.RequestUpdated(request.toPendingVerificationRequest()))
// should also update SAS/QR transaction
getExistingTransaction(request.otherUserId, request.requestId)?.let {
it.state = VerificationTxState.Cancelled(code, true)
getExistingTransaction<KotlinSasTransaction>(request.otherUserId, request.requestId)?.let {
it.state = SasTransactionState.Cancelled(code, true)
txMap[request.otherUserId]?.remove(request.requestId)
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)
}
@ -1592,17 +1782,17 @@ internal class VerificationActor @AssistedInject constructor(
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() }
txInnerMap[tx.transactionId] = tx
eventFlow.emit(VerificationEvent.TransactionAdded(tx))
}
private fun getExistingTransaction(otherUserId: String, transactionId: String): SasV1Transaction? {
return txMap[otherUserId]?.get(transactionId)
private inline fun <reified T : VerificationTransaction> getExistingTransaction(otherUserId: String, transactionId: String): T? {
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 {
val match = it.value.values
.firstOrNull { it.transactionId == transactionId }

View File

@ -17,7 +17,6 @@
package org.matrix.android.sdk.internal.crypto.verification
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.ValidVerificationInfoReady
import org.matrix.android.sdk.api.session.crypto.verification.ValidVerificationInfoRequest
@ -106,6 +105,12 @@ internal sealed class VerificationIntent {
val deferred: CompletableDeferred<VerificationTransaction?>,
) : VerificationIntent()
data class ActionConfirmCodeWasScanned(
val otherUserId: String,
val requestId: String,
val deferred: CompletableDeferred<Unit>,
) : VerificationIntent()
data class OnStartReceived(
val viaRoom: 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.MessageType
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.UserId
import org.matrix.android.sdk.internal.util.time.Clock
@ -56,7 +54,7 @@ internal class VerificationMessageProcessor @Inject constructor(
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}")
// 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
import org.matrix.android.sdk.internal.crypto.verification.KotlinVerificationRequest
enum class EVerificationState {
// outgoing started request
WaitingForReady,
// for incoming
Requested,
// both incoming/outgoing
Ready,
Started,
@ -34,15 +34,16 @@ enum class EVerificationState {
}
// TODO remove that
interface IVerificationRequest{
interface IVerificationRequest {
fun requestId(): String
fun incoming(): Boolean
fun otherUserId(): String
fun roomId(): String?
// target devices in case of to_device self verification
fun targetDevices() : List<String>?
fun targetDevices(): List<String>?
fun state(): EVerificationState
fun ageLocalTs(): Long
@ -53,10 +54,9 @@ interface IVerificationRequest{
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?
}

View File

@ -15,13 +15,6 @@
*/
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.
*/
@ -34,7 +27,7 @@ data class PendingVerificationRequest(
val otherDeviceId: String?,
// in case of verification via room, it will be not null
val roomId: String?,
val transactionId: String,//? = null,
val transactionId: String, // ? = null,
// val requestInfo: ValidVerificationInfoRequest? = null,
// val readyInfo: ValidVerificationInfoReady? = null,
val cancelConclusion: CancelCode? = null,
@ -52,5 +45,4 @@ data class PendingVerificationRequest(
// val isReady: Boolean = readyInfo != null
//
// val isFinished: Boolean = isSuccessful || cancelConclusion != null
}

View File

@ -16,8 +16,22 @@
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 {
fun state(): QRCodeVerificationState
/**
* 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.
*/
suspend fun userHasScannedOtherQrCode(otherQrCodeText: String)
// suspend fun userHasScannedOtherQrCode(otherQrCodeText: String)
/**
* 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.
*/
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)
}
fun state(): SasTransactionState
override fun isSuccessful() = state() is SasTransactionState.Done
fun supportsEmoji(): Boolean
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)
}
fun VerificationEvent.getRequest() : PendingVerificationRequest? {
return when(this) {
fun VerificationEvent.getRequest(): PendingVerificationRequest? {
return when (this) {
is VerificationEvent.RequestAdded -> this.request
is VerificationEvent.RequestUpdated -> this.request
is VerificationEvent.TransactionAdded -> null
@ -32,12 +32,11 @@ fun VerificationEvent.getRequest() : PendingVerificationRequest? {
}
}
fun VerificationEvent.getTransaction() : VerificationTransaction? {
return when(this) {
fun VerificationEvent.getTransaction(): VerificationTransaction? {
return when (this) {
is VerificationEvent.RequestAdded -> null
is VerificationEvent.RequestUpdated -> null
is VerificationEvent.TransactionAdded -> this.transaction
is VerificationEvent.TransactionUpdated -> this.transaction
}
}

View File

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

View File

@ -16,6 +16,35 @@
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 {
/**
* Uninitialized state.
@ -42,24 +71,24 @@ sealed class VerificationTxState {
// object MacSent : VerificationSasTxState()
// object Verifying : VerificationSasTxState()
// I wend a start
object SasStarted : VerificationSasTxState()
// I received a start and it was accepted
object SasAccepted : VerificationSasTxState()
// I received an accept and sent my key
object SasKeySent : VerificationSasTxState()
// Keys exchanged and code ready to be shared
object SasShortCodeReady : VerificationSasTxState()
// 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) : VerificationSasTxState()
// I confirmed the code and sent my mac
object SasMacSent : VerificationSasTxState()
// // I wend a start
// object SasStarted : VerificationSasTxState()
//
// // I received a start and it was accepted
// object SasAccepted : VerificationSasTxState()
//
// // I received an accept and sent my key
// object SasKeySent : VerificationSasTxState()
//
// // Keys exchanged and code ready to be shared
// object SasShortCodeReady : VerificationSasTxState()
//
// // 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) : VerificationSasTxState()
//
// // I confirmed the code and sent my mac
// object SasMacSent : VerificationSasTxState()
// I am done, waiting for other Done
data class Done(val otherDone: Boolean) : VerificationSasTxState()
@ -67,13 +96,13 @@ sealed class VerificationTxState {
/**
* Specific for QR code.
*/
abstract class VerificationQrTxState : VerificationTxState()
/**
* Will be used to ask the user if the other user has correctly scanned.
*/
object QrScannedByOther : VerificationQrTxState()
object WaitingOtherReciprocateConfirm : VerificationQrTxState()
// abstract class VerificationQrTxState : VerificationTxState()
//
// /**
// * Will be used to ask the user if the other user has correctly scanned.
// */
// object QrScannedByOther : VerificationQrTxState()
// object WaitingOtherReciprocateConfirm : VerificationQrTxState()
/**
* Terminal states.

View File

@ -18,7 +18,6 @@ package org.matrix.android.sdk.internal.crypto.verification
import android.os.Handler
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.VerificationService
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.quads.SharedSecureStorageViewModel
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.devtools.RoomDevToolViewModel
import im.vector.app.features.discovery.DiscoverySettingsViewModel
@ -512,15 +509,10 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(MessageActionsViewModel::class)
fun messageActionsViewModelFactory(factory: MessageActionsViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(VerificationChooseMethodViewModel::class)
fun verificationChooseMethodViewModelFactory(factory: VerificationChooseMethodViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(VerificationEmojiCodeViewModel::class)
fun verificationEmojiCodeViewModelFactory(factory: VerificationEmojiCodeViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
// @Binds
// @IntoMap
// @MavericksViewModelKey(VerificationChooseMethodViewModel::class)
// fun verificationChooseMethodViewModelFactory(factory: VerificationChooseMethodViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@ -592,11 +584,10 @@ interface MavericksViewModelModule {
@MavericksViewModelKey(BootstrapSharedViewModel::class)
fun bootstrapSharedViewModelFactory(factory: BootstrapSharedViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap
@MavericksViewModelKey(VerificationBottomSheetViewModel::class)
fun verificationBottomSheetViewModelFactory(factory: VerificationBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
// @Binds
// @IntoMap
// @MavericksViewModelKey(VerificationBottomSheetViewModel::class)
// fun verificationBottomSheetViewModelFactory(factory: VerificationBottomSheetViewModel.Factory): MavericksAssistedViewModelFactory<*, *>
@Binds
@IntoMap

View File

@ -25,7 +25,6 @@ import im.vector.app.features.popup.PopupAlertManager
import im.vector.app.features.session.coroutineScope
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancel
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.IncomingRoomKeyRequest
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.VerificationEvent
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.VerificationTxState
import timber.log.Timber
import javax.inject.Inject
import javax.inject.Singleton
@ -275,8 +274,8 @@ class KeyRequestHandler @Inject constructor(
override fun transactionUpdated(tx: VerificationTransaction) {
if (tx is SasVerificationTransaction) {
val state = tx.state
if (state == VerificationTxState.Verified) {
val state = tx.state()
if (state is SasTransactionState.Done) {
// ok it's verified, see if we have key request for that
shareAllSessions("${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.crypto.verification.EVerificationState
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.VerificationService
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.util.toMatrixItem
import timber.log.Timber
@ -89,8 +90,9 @@ class IncomingVerificationRequestHandler @Inject constructor(
// TODO maybe check also if
val uid = "kvr_${tx.transactionId}"
// TODO we don't have that anymore? as it has to be requested first?
when (tx.state) {
is VerificationTxState.SasStarted -> {
if (tx !is SasVerificationTransaction) return
when (tx.state()) {
is SasTransactionState.SasStarted -> {
// Add a notification for every incoming request
// val user = session.getUserOrDefault(tx.otherUserId).toMatrixItem()
// val name = user.getBestName()
@ -139,7 +141,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
// }
// popupAlertManager.postVectorAlert(alert)
}
is VerificationTxState.TerminalTxState -> {
is SasTransactionState.Done -> {
// cancel related notification
popupAlertManager.cancelAlert(uid)
}
@ -186,7 +188,7 @@ class IncomingVerificationRequestHandler @Inject constructor(
val roomId = pr.roomId
if (roomId.isNullOrBlank()) {
// TODO
//it.navigator.waitSessionVerification(it)
// it.navigator.waitSessionVerification(it)
} else {
it.navigator.openRoom(
context = it,
@ -213,9 +215,9 @@ class IncomingVerificationRequestHandler @Inject constructor(
override fun verificationRequestUpdated(pr: PendingVerificationRequest) {
// If an incoming request is readied (by another device?) we should discard the alert
if (pr.isIncoming && (pr.state == EVerificationState.HandledByOtherSession
|| pr.state == EVerificationState.Started
|| pr.state == EVerificationState.WeStarted)) {
if (pr.isIncoming && (pr.state == EVerificationState.HandledByOtherSession ||
pr.state == EVerificationState.Started ||
pr.state == EVerificationState.WeStarted)) {
popupAlertManager.cancelAlert(uniqueIdForVerificationRequest(pr))
}
}

View File

@ -1,266 +1,228 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification
import android.app.Activity
import android.app.Dialog
import android.os.Bundle
import android.os.Parcelable
import android.view.KeyEvent
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import com.google.android.material.dialog.MaterialAlertDialogBuilder
import dagger.hilt.android.AndroidEntryPoint
import im.vector.app.R
import im.vector.app.core.extensions.commitTransaction
import im.vector.app.core.extensions.registerStartForActivityResult
import im.vector.app.core.extensions.toMvRxBundle
import im.vector.app.core.platform.VectorBaseActivity
import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetVerificationBinding
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.VerificationNotMeFragment
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.emoji.VerificationEmojiCodeFragment
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.request.VerificationRequestFragment
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer
import im.vector.app.features.settings.VectorSettingsActivity
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.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.RoomEncryptionTrustLevel
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.VerificationTxState
import timber.log.Timber
import javax.inject.Inject
import kotlin.reflect.KClass
@AndroidEntryPoint
class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetVerificationBinding>() {
@Parcelize
data class VerificationArgs(
val otherUserId: String,
// might be null for self verification if there is no device to request to
// in this case you could use 4S (or reset all)
val verificationId: String?,
// val verificationLocalId: String? = null,
val roomId: String? = null,
// Special mode where UX should show loading wheel until other session sends a request/tx
val selfVerificationMode: Boolean = false
) : Parcelable
override val showExpanded = true
@Inject
lateinit var avatarRenderer: AvatarRenderer
private val viewModel by fragmentViewModel(VerificationBottomSheetViewModel::class)
override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationBinding {
return BottomSheetVerificationBinding.inflate(inflater, container, false)
}
init {
isCancelable = false
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel.observeViewEvents {
when (it) {
is VerificationBottomSheetViewEvents.Dismiss -> dismiss()
is VerificationBottomSheetViewEvents.AccessSecretStore -> {
secretStartForActivityResult.launch(
SharedSecureStorageActivity.newReadIntent(
requireContext(),
null, // use default key
listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME),
SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
)
)
}
is VerificationBottomSheetViewEvents.ModalError -> {
MaterialAlertDialogBuilder(requireContext())
.setTitle(getString(R.string.dialog_title_error))
.setMessage(it.errorMessage)
.setCancelable(false)
.setPositiveButton(R.string.ok, null)
.show()
Unit
}
VerificationBottomSheetViewEvents.GoToSettings -> {
dismiss()
(activity as? VectorBaseActivity<*>)?.let { activity ->
activity.navigator.openSettings(activity, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY)
}
}
}
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).apply {
setOnKeyListener { _, keyCode, keyEvent ->
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
viewModel.queryCancel()
true
} else {
false
}
}
}
}
private val secretStartForActivityResult = registerStartForActivityResult { activityResult ->
if (activityResult.resultCode == Activity.RESULT_OK) {
val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
val reset = activityResult.data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false
if (result != null) {
viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS))
} else if (reset) {
// all have been reset, so we are verified?
viewModel.handle(VerificationAction.SecuredStorageHasBeenReset)
}
} else {
viewModel.handle(VerificationAction.CancelledFromSsss)
}
}
override fun invalidate() = withState(viewModel) { state ->
avatarRenderer.render(state.otherUserMxItem, views.otherUserAvatarImageView)
if (state.isMe) {
if (state.sasTransactionState == VerificationTxState.Verified ||
state.qrTransactionState == VerificationTxState.Verified ||
state.verifiedFromPrivateKeys) {
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
} else {
views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
}
views.otherUserNameText.text = getString(
if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session
)
} else {
if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
views.otherUserNameText.text = getString(R.string.verification_verified_user, state.otherUserMxItem.getBestName())
views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
} else {
views.otherUserNameText.text = getString(R.string.verification_verify_user, state.otherUserMxItem.getBestName())
views.otherUserShield.render(null)
}
}
if (state.quadSHasBeenReset) {
showFragment(
VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(
isSuccessFull = true,
isMe = true,
cancelReason = null
)
)
return@withState
}
if (state.userThinkItsNotHim) {
views.otherUserNameText.text = getString(R.string.dialog_title_warning)
showFragment(VerificationNotMeFragment::class)
return@withState
}
if (state.userWantsToCancel) {
views.otherUserNameText.text = getString(R.string.are_you_sure)
showFragment(VerificationCancelFragment::class)
return@withState
}
if (state.selfVerificationMode && state.verifyingFrom4S) {
showFragment(QuadSLoadingFragment::class)
return@withState
}
if (state.selfVerificationMode && state.verifiedFromPrivateKeys) {
showFragment(
VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(true, null, state.isMe)
)
return@withState
}
// Did the request result in a SAS transaction?
if (state.sasTransactionState != null) {
when (state.sasTransactionState) {
VerificationTxState.None,
VerificationTxState.SasStarted,
VerificationTxState.SasKeySent,
VerificationTxState.SasShortCodeReady,
VerificationTxState.SasMacSent,
is VerificationTxState.SasMacReceived,
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 -> {
// /*
// * Copyright 2019 New Vector Ltd
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
// package im.vector.app.features.crypto.verification
//
// import android.app.Activity
// import android.app.Dialog
// import android.os.Bundle
// import android.os.Parcelable
// import android.view.KeyEvent
// import android.view.LayoutInflater
// import android.view.View
// import android.view.ViewGroup
// import androidx.fragment.app.Fragment
// import com.airbnb.mvrx.fragmentViewModel
// import com.airbnb.mvrx.withState
// import com.google.android.material.dialog.MaterialAlertDialogBuilder
// import dagger.hilt.android.AndroidEntryPoint
// import im.vector.app.R
// import im.vector.app.core.extensions.commitTransaction
// import im.vector.app.core.extensions.registerStartForActivityResult
// import im.vector.app.core.extensions.toMvRxBundle
// import im.vector.app.core.platform.VectorBaseActivity
// import im.vector.app.core.platform.VectorBaseBottomSheetDialogFragment
// import im.vector.app.databinding.BottomSheetVerificationBinding
// 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.VerificationNotMeFragment
// 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.emoji.VerificationEmojiCodeFragment
// 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.request.VerificationRequestFragment
// import im.vector.app.features.displayname.getBestName
// import im.vector.app.features.home.AvatarRenderer
// import im.vector.app.features.settings.VectorSettingsActivity
// 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.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.RoomEncryptionTrustLevel
// 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.VerificationTxState
// import timber.log.Timber
// import javax.inject.Inject
// import kotlin.reflect.KClass
//
// @AndroidEntryPoint
// class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetVerificationBinding>() {
//
// @Parcelize
// data class VerificationArgs(
// val otherUserId: String,
// // might be null for self verification if there is no device to request to
// // in this case you could use 4S (or reset all)
// val verificationId: String?,
// // val verificationLocalId: String? = null,
// val roomId: String? = null,
// // Special mode where UX should show loading wheel until other session sends a request/tx
// val selfVerificationMode: Boolean = false
// ) : Parcelable
//
// override val showExpanded = true
//
// @Inject
// lateinit var avatarRenderer: AvatarRenderer
//
// private val viewModel by fragmentViewModel(VerificationBottomSheetViewModel::class)
//
// override fun getBinding(inflater: LayoutInflater, container: ViewGroup?): BottomSheetVerificationBinding {
// return BottomSheetVerificationBinding.inflate(inflater, container, false)
// }
//
// init {
// isCancelable = false
// }
//
// override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
// super.onViewCreated(view, savedInstanceState)
//
// viewModel.observeViewEvents {
// when (it) {
// is VerificationBottomSheetViewEvents.Dismiss -> dismiss()
// is VerificationBottomSheetViewEvents.AccessSecretStore -> {
// secretStartForActivityResult.launch(
// SharedSecureStorageActivity.newReadIntent(
// requireContext(),
// null, // use default key
// listOf(MASTER_KEY_SSSS_NAME, USER_SIGNING_KEY_SSSS_NAME, SELF_SIGNING_KEY_SSSS_NAME, KEYBACKUP_SECRET_SSSS_NAME),
// SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS
// )
// )
// }
// is VerificationBottomSheetViewEvents.ModalError -> {
// MaterialAlertDialogBuilder(requireContext())
// .setTitle(getString(R.string.dialog_title_error))
// .setMessage(it.errorMessage)
// .setCancelable(false)
// .setPositiveButton(R.string.ok, null)
// .show()
// Unit
// }
// VerificationBottomSheetViewEvents.GoToSettings -> {
// dismiss()
// (activity as? VectorBaseActivity<*>)?.let { activity ->
// activity.navigator.openSettings(activity, VectorSettingsActivity.EXTRA_DIRECT_ACCESS_SECURITY_PRIVACY)
// }
// }
// }
// }
// }
//
// override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
// return super.onCreateDialog(savedInstanceState).apply {
// setOnKeyListener { _, keyCode, keyEvent ->
// if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
// viewModel.queryCancel()
// true
// } else {
// false
// }
// }
// }
// }
//
// private val secretStartForActivityResult = registerStartForActivityResult { activityResult ->
// if (activityResult.resultCode == Activity.RESULT_OK) {
// val result = activityResult.data?.getStringExtra(SharedSecureStorageActivity.EXTRA_DATA_RESULT)
// val reset = activityResult.data?.getBooleanExtra(SharedSecureStorageActivity.EXTRA_DATA_RESET, false) ?: false
// if (result != null) {
// viewModel.handle(VerificationAction.GotResultFromSsss(result, SharedSecureStorageActivity.DEFAULT_RESULT_KEYSTORE_ALIAS))
// } else if (reset) {
// // all have been reset, so we are verified?
// viewModel.handle(VerificationAction.SecuredStorageHasBeenReset)
// }
// } else {
// viewModel.handle(VerificationAction.CancelledFromSsss)
// }
// }
//
// override fun invalidate() = withState(viewModel) { state ->
// avatarRenderer.render(state.otherUserMxItem, views.otherUserAvatarImageView)
// if (state.isMe) {
// if (state.sasTransactionState == VerificationTxState.Verified ||
// state.qrTransactionState == VerificationTxState.Verified ||
// state.verifiedFromPrivateKeys) {
// views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
// } else {
// views.otherUserShield.render(RoomEncryptionTrustLevel.Warning)
// }
// views.otherUserNameText.text = getString(
// if (state.selfVerificationMode) R.string.crosssigning_verify_this_session else R.string.crosssigning_verify_session
// )
// } else {
// if (state.sasTransactionState == VerificationTxState.Verified || state.qrTransactionState == VerificationTxState.Verified) {
// views.otherUserNameText.text = getString(R.string.verification_verified_user, state.otherUserMxItem.getBestName())
// views.otherUserShield.render(RoomEncryptionTrustLevel.Trusted)
// } else {
// views.otherUserNameText.text = getString(R.string.verification_verify_user, state.otherUserMxItem.getBestName())
// views.otherUserShield.render(null)
// }
// }
//
// if (state.quadSHasBeenReset) {
// showFragment(
// VerificationConclusionFragment::class,
// VerificationConclusionFragment.Args(
// isSuccessFull = true,
// isMe = true,
// cancelReason = null
// )
// )
// return@withState
// }
//
// if (state.userThinkItsNotHim) {
// views.otherUserNameText.text = getString(R.string.dialog_title_warning)
// showFragment(VerificationNotMeFragment::class)
// return@withState
// }
//
// if (state.userWantsToCancel) {
// views.otherUserNameText.text = getString(R.string.are_you_sure)
// showFragment(VerificationCancelFragment::class)
// return@withState
// }
//
// if (state.selfVerificationMode && state.verifyingFrom4S) {
// showFragment(QuadSLoadingFragment::class)
// return@withState
// }
// if (state.selfVerificationMode && state.verifiedFromPrivateKeys) {
// showFragment(
// VerificationConclusionFragment::class,
// VerificationConclusionFragment.Args(true, null, state.isMe)
// )
// return@withState
// }
//
// // Did the request result in a SAS transaction?
// if (state.sasTransactionState != null) {
// when (state.sasTransactionState) {
//
// VerificationTxState.None,
// VerificationTxState.SasStarted,
// VerificationTxState.SasKeySent,
// VerificationTxState.SasShortCodeReady,
// VerificationTxState.SasMacSent,
// is VerificationTxState.SasMacReceived,
// VerificationTxState.SasAccepted -> {
// showFragment(
// VerificationEmojiCodeFragment::class,
// VerificationArgs(
@ -284,156 +246,194 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment<BottomSheetV
// )
// }
// else -> Unit
}
return@withState
}
when (state.qrTransactionState) {
is VerificationTxState.QrScannedByOther -> {
showFragment(VerificationQrScannedByOtherFragment::class)
return@withState
}
//TODO
// is VerificationTxState.Started,
is VerificationTxState.WaitingOtherReciprocateConfirm -> {
showFragment(
VerificationQRWaitingFragment::class,
VerificationQRWaitingFragment.Args(
isMe = state.isMe,
otherUserName = state.otherUserMxItem.getBestName()
)
)
return@withState
}
is VerificationTxState.Verified -> {
showFragment(
VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(true, null, state.isMe)
)
return@withState
}
is VerificationTxState.Cancelled -> {
showFragment(
VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe)
)
return@withState
}
else -> Unit
}
// At this point there is no SAS transaction for this request
// Transaction has not yet started
if (state.pendingRequest.invoke()?.cancelConclusion != null) {
// The request has been declined, we should dismiss
views.otherUserNameText.text = getString(R.string.verification_cancelled)
showFragment(
VerificationConclusionFragment::class,
VerificationConclusionFragment.Args(
isSuccessFull = false,
cancelReason = state.pendingRequest.invoke()?.cancelConclusion?.value ?: CancelCode.User.value,
isMe = state.isMe
)
)
return@withState
}
// 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")
if (state.pendingRequest.invoke()?.state == EVerificationState.Ready) {
Timber.v("## SAS show bottom sheet for outgoing and ready request")
// Show choose method fragment with waiting
showFragment(
VerificationChooseMethodFragment::class,
VerificationArgs(
otherUserId = state.otherUserId,
verificationId = state.pendingRequest.invoke()?.transactionId
)
)
} else {
// Stay on the start fragment
showFragment(
VerificationRequestFragment::class,
VerificationArgs(
otherUserId = state.otherUserId,
verificationId = state.pendingRequest.invoke()?.transactionId,
)
)
}
} else if (state.pendingRequest.invoke()?.isIncoming == true) {
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 {
setArguments(
VerificationArgs(
otherUserId = otherUserId,
verificationId = transactionId,
)
)
}
}
// fun forSelfVerification(session: Session): VerificationBottomSheet {
// // 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(
// // 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
// }
//
// return@withState
// }
//
// when (state.qrTransactionState) {
// is VerificationTxState.QrScannedByOther -> {
// showFragment(VerificationQrScannedByOtherFragment::class)
// return@withState
// }
// //TODO
// // is VerificationTxState.Started,
// is VerificationTxState.WaitingOtherReciprocateConfirm -> {
// showFragment(
// VerificationQRWaitingFragment::class,
// VerificationQRWaitingFragment.Args(
// isMe = state.isMe,
// otherUserName = state.otherUserMxItem.getBestName()
// )
// )
// return@withState
// }
// is VerificationTxState.Verified -> {
// showFragment(
// VerificationConclusionFragment::class,
// VerificationConclusionFragment.Args(true, null, state.isMe)
// )
// return@withState
// }
// is VerificationTxState.Cancelled -> {
// showFragment(
// VerificationConclusionFragment::class,
// VerificationConclusionFragment.Args(false, state.qrTransactionState.cancelCode.value, state.isMe)
// )
// return@withState
// }
// else -> Unit
// }
//
// // At this point there is no SAS transaction for this request
//
// // Transaction has not yet started
// if (state.pendingRequest.invoke()?.cancelConclusion != null) {
// // The request has been declined, we should dismiss
// views.otherUserNameText.text = getString(R.string.verification_cancelled)
// showFragment(
// VerificationConclusionFragment::class,
// VerificationConclusionFragment.Args(
// isSuccessFull = false,
// cancelReason = state.pendingRequest.invoke()?.cancelConclusion?.value ?: CancelCode.User.value,
// isMe = state.isMe
// )
// )
// return@withState
// }
//
// // 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")
// if (state.pendingRequest.invoke()?.state == EVerificationState.Ready) {
// Timber.v("## SAS show bottom sheet for outgoing and ready request")
// // Show choose method fragment with waiting
// showFragment(
// VerificationChooseMethodFragment::class,
// VerificationArgs(
// otherUserId = state.otherUserId,
// verificationId = state.pendingRequest.invoke()?.transactionId
// )
// )
// } else {
// // Stay on the start fragment
// showFragment(
// VerificationRequestFragment::class,
// VerificationArgs(
// otherUserId = state.otherUserId,
// verificationId = state.pendingRequest.invoke()?.transactionId,
// )
// )
// }
// } else if (state.pendingRequest.invoke()?.isIncoming == true) {
// 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 {
// setArguments(
// VerificationArgs(
// otherUserId = session.myUserId,
// selfVerificationMode = true
// otherUserId = otherUserId,
// verificationId = transactionId,
// )
// )
// }
// }
// fun forSelfVerification(session: Session, outgoingRequest: String): VerificationBottomSheet {
// return VerificationBottomSheet().apply {
// setArguments(
// VerificationArgs(
// otherUserId = session.myUserId,
// selfVerificationMode = true,
// verificationId = outgoingRequest
// )
// )
//
// // fun forSelfVerification(session: Session): VerificationBottomSheet {
// // return VerificationBottomSheet().apply {
// // setArguments(
// // VerificationArgs(
// // otherUserId = session.myUserId,
// // selfVerificationMode = true
// // )
// // )
// // }
// // }
//
// // 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
// current = current.parent as? View
// }
// return null
// }
//
// // fun View.getParentCoordinatorLayout(): CoordinatorLayout? {
// // var current = this as? View
// // while (current != 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
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification.cancel
import androidx.core.text.toSpannable
import com.airbnb.epoxy.EpoxyController
import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
import im.vector.app.core.utils.colorizeMatchingText
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.bottomSheetVerificationNoticeItem
import im.vector.app.features.displayname.getBestName
import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import javax.inject.Inject
class VerificationCancelController @Inject constructor(
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider
) : EpoxyController() {
var listener: Listener? = null
private var viewState: VerificationBottomSheetViewState? = null
fun update(viewState: VerificationBottomSheetViewState) {
this.viewState = viewState
requestModelBuild()
}
override fun buildModels() {
val state = viewState ?: return
val host = this
if (state.isMe) {
if (state.currentDeviceCanCrossSign) {
bottomSheetVerificationNoticeItem {
id("notice")
notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_trusted).toEpoxyCharSequence())
}
} else {
bottomSheetVerificationNoticeItem {
id("notice")
notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted).toEpoxyCharSequence())
}
}
} else {
val otherUserID = state.otherUserId
val otherDisplayName = state.otherUserMxItem.getBestName()
bottomSheetVerificationNoticeItem {
id("notice")
notice(
EpoxyCharSequence(
host.stringProvider.getString(R.string.verify_cancel_other, otherDisplayName, otherUserID)
.toSpannable()
.colorizeMatchingText(otherUserID, host.colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
)
)
}
}
bottomSheetDividerItem {
id("sep0")
}
bottomSheetVerificationActionItem {
id("cancel")
title(host.stringProvider.getString(R.string.action_skip))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
listener { host.listener?.onTapCancel() }
}
bottomSheetDividerItem {
id("sep1")
}
bottomSheetVerificationActionItem {
id("continue")
title(host.stringProvider.getString(R.string._continue))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
listener { host.listener?.onTapContinue() }
}
}
interface Listener {
fun onTapCancel()
fun onTapContinue()
}
}
// /*
// * Copyright (c) 2020 New Vector Ltd
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
// package im.vector.app.features.crypto.verification.cancel
//
// import androidx.core.text.toSpannable
// import com.airbnb.epoxy.EpoxyController
// import im.vector.app.R
// import im.vector.app.core.epoxy.bottomSheetDividerItem
// import im.vector.app.core.resources.ColorProvider
// import im.vector.app.core.resources.StringProvider
// import im.vector.app.core.utils.colorizeMatchingText
// 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.bottomSheetVerificationNoticeItem
// import im.vector.app.features.displayname.getBestName
// import im.vector.lib.core.utils.epoxy.charsequence.EpoxyCharSequence
// import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
// import javax.inject.Inject
//
// class VerificationCancelController @Inject constructor(
// private val stringProvider: StringProvider,
// private val colorProvider: ColorProvider
// ) : EpoxyController() {
//
// var listener: Listener? = null
//
// private var viewState: VerificationBottomSheetViewState? = null
//
// fun update(viewState: VerificationBottomSheetViewState) {
// this.viewState = viewState
// requestModelBuild()
// }
//
// override fun buildModels() {
// val state = viewState ?: return
// val host = this
// if (state.isMe) {
// if (state.currentDeviceCanCrossSign) {
// bottomSheetVerificationNoticeItem {
// id("notice")
// notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_trusted).toEpoxyCharSequence())
// }
// } else {
// bottomSheetVerificationNoticeItem {
// id("notice")
// notice(host.stringProvider.getString(R.string.verify_cancel_self_verification_from_untrusted).toEpoxyCharSequence())
// }
// }
// } else {
// val otherUserID = state.otherUserId
// val otherDisplayName = state.otherUserMxItem.getBestName()
// bottomSheetVerificationNoticeItem {
// id("notice")
// notice(
// EpoxyCharSequence(
// host.stringProvider.getString(R.string.verify_cancel_other, otherDisplayName, otherUserID)
// .toSpannable()
// .colorizeMatchingText(otherUserID, host.colorProvider.getColorFromAttribute(R.attr.vctr_notice_text_color))
// )
// )
// }
// }
//
// bottomSheetDividerItem {
// id("sep0")
// }
//
// bottomSheetVerificationActionItem {
// id("cancel")
// title(host.stringProvider.getString(R.string.action_skip))
// titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
// iconRes(R.drawable.ic_arrow_right)
// iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorError))
// listener { host.listener?.onTapCancel() }
// }
//
// bottomSheetDividerItem {
// id("sep1")
// }
//
// bottomSheetVerificationActionItem {
// id("continue")
// title(host.stringProvider.getString(R.string._continue))
// titleColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
// iconRes(R.drawable.ic_arrow_right)
// iconColor(host.colorProvider.getColorFromAttribute(R.attr.colorPrimary))
// listener { host.listener?.onTapContinue() }
// }
// }
//
// interface Listener {
// fun onTapCancel()
// fun onTapContinue()
// }
// }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,172 +1,172 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification.choose
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.EntryPoints
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import org.matrix.android.sdk.api.extensions.orFalse
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.PendingVerificationRequest
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.VerificationService
import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
data class VerificationChooseMethodViewState(
val otherUserId: String = "",
val transactionId: String = "",
val otherCanShowQrCode: Boolean = false,
val otherCanScanQrCode: Boolean = false,
val qrCodeText: String? = null,
val sasModeAvailable: Boolean = false,
val isMe: Boolean = false,
val canCrossSign: Boolean = false,
val isReadySent: Boolean = false
) : MavericksState
class VerificationChooseMethodViewModel @AssistedInject constructor(
@Assisted initialState: VerificationChooseMethodViewState,
private val session: Session
) : VectorViewModel<VerificationChooseMethodViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener {
init {
// session.cryptoService().verificationService().addListener(this)
session.cryptoService().verificationService()
.requestEventFlow()
.onEach {
when (it) {
// TODO check transaction id
is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request)
is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request)
is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction)
is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction)
}
}
.launchIn(viewModelScope)
viewModelScope.launch {
val verificationService = session.cryptoService().verificationService()
val pvr = verificationService.getExistingVerificationRequest(initialState.otherUserId, initialState.transactionId)
// Get the QR code now, because transaction is already created, so transactionCreated() will not be called
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) {
// /*
// * Copyright 2019 New Vector Ltd
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
// package im.vector.app.features.crypto.verification.choose
//
// import com.airbnb.mvrx.MavericksState
// import com.airbnb.mvrx.MavericksViewModelFactory
// import com.airbnb.mvrx.ViewModelContext
// import dagger.assisted.Assisted
// import dagger.assisted.AssistedFactory
// import dagger.assisted.AssistedInject
// import dagger.hilt.EntryPoints
// import im.vector.app.core.di.MavericksAssistedViewModelFactory
// import im.vector.app.core.di.SingletonEntryPoint
// import im.vector.app.core.di.hiltMavericksViewModelFactory
// import im.vector.app.core.platform.EmptyAction
// import im.vector.app.core.platform.EmptyViewEvents
// import im.vector.app.core.platform.VectorViewModel
// import im.vector.app.features.crypto.verification.VerificationBottomSheet
// import kotlinx.coroutines.flow.launchIn
// import kotlinx.coroutines.flow.onEach
// import kotlinx.coroutines.launch
// import org.matrix.android.sdk.api.extensions.orFalse
// 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.PendingVerificationRequest
// 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.VerificationService
// import org.matrix.android.sdk.api.session.crypto.verification.VerificationTransaction
//
// data class VerificationChooseMethodViewState(
// val otherUserId: String = "",
// val transactionId: String = "",
// val otherCanShowQrCode: Boolean = false,
// val otherCanScanQrCode: Boolean = false,
// val qrCodeText: String? = null,
// val sasModeAvailable: Boolean = false,
// val isMe: Boolean = false,
// val canCrossSign: Boolean = false,
// val isReadySent: Boolean = false
// ) : MavericksState
//
// class VerificationChooseMethodViewModel @AssistedInject constructor(
// @Assisted initialState: VerificationChooseMethodViewState,
// private val session: Session
// ) : VectorViewModel<VerificationChooseMethodViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener {
//
// init {
// // session.cryptoService().verificationService().addListener(this)
//
// session.cryptoService().verificationService()
// .requestEventFlow()
// .onEach {
// when (it) {
// // TODO check transaction id
// is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request)
// is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request)
// is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction)
// is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction)
// }
// }
// .launchIn(viewModelScope)
//
// viewModelScope.launch {
//
// val verificationService = session.cryptoService().verificationService()
// val pvr = verificationService.getExistingVerificationRequest(initialState.otherUserId, initialState.transactionId)
//
// // Get the QR code now, because transaction is already created, so transactionCreated() will not be called
// val qrCodeVerificationTransaction = verificationService.getExistingTransaction(initialState.otherUserId, initialState.transactionId)
//
// setState {
// copy(
// qrCodeText = tx.qrCodeText
// 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 verificationRequestCreated(pr: PendingVerificationRequest) {
verificationRequestUpdated(pr)
}
override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
viewModelScope.launch {
val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
setState {
copy(
otherCanShowQrCode = pvr?.otherCanShowQrCode.orFalse(),
otherCanScanQrCode = pvr?.otherCanScanQrCode.orFalse(),
sasModeAvailable = pvr?.isSasSupported.orFalse(),
isReadySent = pvr?.state == EVerificationState.Ready,
qrCodeText = pvr?.qrCodeText
)
}
}
}
@AssistedFactory
interface Factory : MavericksAssistedViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> {
override fun create(initialState: VerificationChooseMethodViewState): VerificationChooseMethodViewModel
}
companion object : MavericksViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> by hiltMavericksViewModelFactory() {
override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState {
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)
// 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(
otherUserId = args.otherUserId,
// 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) {}
}
//
//
// }
//
// override fun transactionCreated(tx: VerificationTransaction) {
// transactionUpdated(tx)
// }
//
// override fun transactionUpdated(tx: VerificationTransaction) = withState { state ->
// // if (tx.transactionId == state.transactionId && tx is QrCodeVerificationTransaction) {
// // setState {
// // copy(
// // qrCodeText = tx.qrCodeText
// // )
// // }
// // }
// }
//
// override fun verificationRequestCreated(pr: PendingVerificationRequest) {
// verificationRequestUpdated(pr)
// }
//
// override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state ->
// viewModelScope.launch {
// val pvr = session.cryptoService().verificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId)
//
// setState {
// copy(
// otherCanShowQrCode = pvr?.otherCanShowQrCode.orFalse(),
// otherCanScanQrCode = pvr?.otherCanScanQrCode.orFalse(),
// sasModeAvailable = pvr?.isSasSupported.orFalse(),
// isReadySent = pvr?.state == EVerificationState.Ready,
// qrCodeText = pvr?.qrCodeText
// )
// }
// }
// }
//
// @AssistedFactory
// interface Factory : MavericksAssistedViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> {
// override fun create(initialState: VerificationChooseMethodViewState): VerificationChooseMethodViewModel
// }
//
// companion object : MavericksViewModelFactory<VerificationChooseMethodViewModel, VerificationChooseMethodViewState> by hiltMavericksViewModelFactory() {
//
// override fun initialState(viewModelContext: ViewModelContext): VerificationChooseMethodViewState {
// 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)
//
// // 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(
// otherUserId = args.otherUserId,
// // 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
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification.conclusion
import com.airbnb.epoxy.EpoxyController
import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
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.bottomSheetVerificationNoticeItem
import im.vector.app.features.html.EventHtmlRenderer
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject
class VerificationConclusionController @Inject constructor(
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider,
private val eventHtmlRenderer: EventHtmlRenderer
) : EpoxyController() {
var listener: Listener? = null
private var viewState: VerificationConclusionViewState? = null
fun update(viewState: VerificationConclusionViewState) {
this.viewState = viewState
requestModelBuild()
}
override fun buildModels() {
val state = viewState ?: return
val host = this
when (state.conclusionState) {
ConclusionState.SUCCESS -> {
bottomSheetVerificationNoticeItem {
id("notice")
notice(
host.stringProvider.getString(
if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice
else R.string.verification_conclusion_ok_notice
)
.toEpoxyCharSequence()
)
}
bottomSheetVerificationBigImageItem {
id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
}
bottomDone()
}
ConclusionState.WARNING -> {
bottomSheetVerificationNoticeItem {
id("notice")
notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure).toEpoxyCharSequence())
}
bottomSheetVerificationBigImageItem {
id("image")
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Warning)
}
bottomSheetVerificationNoticeItem {
id("warning_notice")
notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised)).toEpoxyCharSequence())
}
bottomGotIt()
}
ConclusionState.INVALID_QR_CODE -> {
bottomSheetVerificationNoticeItem {
id("invalid_qr")
notice(host.stringProvider.getString(R.string.verify_invalid_qr_notice).toEpoxyCharSequence())
}
bottomGotIt()
}
ConclusionState.CANCELLED -> {
bottomSheetVerificationNoticeItem {
id("notice_cancelled")
notice(host.stringProvider.getString(R.string.verify_cancelled_notice).toEpoxyCharSequence())
}
bottomGotIt()
}
}
}
private fun bottomDone() {
val host = this
bottomSheetDividerItem {
id("sep0")
}
bottomSheetVerificationActionItem {
id("done")
title(host.stringProvider.getString(R.string.done))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onButtonTapped(true) }
}
}
private fun bottomGotIt() {
val host = this
bottomSheetDividerItem {
id("sep0")
}
bottomSheetVerificationActionItem {
id("got_it")
title(host.stringProvider.getString(R.string.sas_got_it))
titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
iconRes(R.drawable.ic_arrow_right)
iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
listener { host.listener?.onButtonTapped(false) }
}
}
interface Listener {
fun onButtonTapped(success: Boolean)
}
}
// /*
// * Copyright 2020 New Vector Ltd
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
// package im.vector.app.features.crypto.verification.conclusion
//
// import com.airbnb.epoxy.EpoxyController
// import im.vector.app.R
// import im.vector.app.core.epoxy.bottomSheetDividerItem
// import im.vector.app.core.resources.ColorProvider
// import im.vector.app.core.resources.StringProvider
// 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.bottomSheetVerificationNoticeItem
// import im.vector.app.features.html.EventHtmlRenderer
// import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
// import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
// import javax.inject.Inject
//
// class VerificationConclusionController @Inject constructor(
// private val stringProvider: StringProvider,
// private val colorProvider: ColorProvider,
// private val eventHtmlRenderer: EventHtmlRenderer
// ) : EpoxyController() {
//
// var listener: Listener? = null
//
// private var viewState: VerificationConclusionViewState? = null
//
// fun update(viewState: VerificationConclusionViewState) {
// this.viewState = viewState
// requestModelBuild()
// }
//
// override fun buildModels() {
// val state = viewState ?: return
// val host = this
//
// when (state.conclusionState) {
// ConclusionState.SUCCESS -> {
// bottomSheetVerificationNoticeItem {
// id("notice")
// notice(
// host.stringProvider.getString(
// if (state.isSelfVerification) R.string.verification_conclusion_ok_self_notice
// else R.string.verification_conclusion_ok_notice
// )
// .toEpoxyCharSequence()
// )
// }
//
// bottomSheetVerificationBigImageItem {
// id("image")
// roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
// }
//
// bottomDone()
// }
// ConclusionState.WARNING -> {
// bottomSheetVerificationNoticeItem {
// id("notice")
// notice(host.stringProvider.getString(R.string.verification_conclusion_not_secure).toEpoxyCharSequence())
// }
//
// bottomSheetVerificationBigImageItem {
// id("image")
// roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Warning)
// }
//
// bottomSheetVerificationNoticeItem {
// id("warning_notice")
// notice(host.eventHtmlRenderer.render(host.stringProvider.getString(R.string.verification_conclusion_compromised)).toEpoxyCharSequence())
// }
//
// bottomGotIt()
// }
// ConclusionState.INVALID_QR_CODE -> {
// bottomSheetVerificationNoticeItem {
// id("invalid_qr")
// notice(host.stringProvider.getString(R.string.verify_invalid_qr_notice).toEpoxyCharSequence())
// }
//
// bottomGotIt()
// }
// ConclusionState.CANCELLED -> {
// bottomSheetVerificationNoticeItem {
// id("notice_cancelled")
// notice(host.stringProvider.getString(R.string.verify_cancelled_notice).toEpoxyCharSequence())
// }
//
// bottomGotIt()
// }
// }
// }
//
// private fun bottomDone() {
// val host = this
// bottomSheetDividerItem {
// id("sep0")
// }
//
// bottomSheetVerificationActionItem {
// id("done")
// title(host.stringProvider.getString(R.string.done))
// titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
// iconRes(R.drawable.ic_arrow_right)
// iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
// listener { host.listener?.onButtonTapped(true) }
// }
// }
//
// private fun bottomGotIt() {
// val host = this
// bottomSheetDividerItem {
// id("sep0")
// }
//
// bottomSheetVerificationActionItem {
// id("got_it")
// title(host.stringProvider.getString(R.string.sas_got_it))
// titleColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
// iconRes(R.drawable.ic_arrow_right)
// iconColor(host.colorProvider.getColorFromAttribute(R.attr.vctr_content_primary))
// listener { host.listener?.onButtonTapped(false) }
// }
// }
//
// interface Listener {
// fun onButtonTapped(success: Boolean)
// }
// }

View File

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

View File

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

View File

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

View File

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

View File

@ -1,209 +1,209 @@
/*
* Copyright 2019 New Vector Ltd
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification.emoji
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.MavericksViewModelFactory
import com.airbnb.mvrx.Success
import com.airbnb.mvrx.Uninitialized
import com.airbnb.mvrx.ViewModelContext
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import dagger.hilt.EntryPoints
import im.vector.app.core.di.MavericksAssistedViewModelFactory
import im.vector.app.core.di.SingletonEntryPoint
import im.vector.app.core.di.hiltMavericksViewModelFactory
import im.vector.app.core.platform.EmptyAction
import im.vector.app.core.platform.EmptyViewEvents
import im.vector.app.core.platform.VectorViewModel
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
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.SasVerificationTransaction
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.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.util.MatrixItem
import org.matrix.android.sdk.api.util.toMatrixItem
data class VerificationEmojiCodeViewState(
val transactionId: String?,
val otherUser: MatrixItem,
val supportsEmoji: Boolean = true,
val emojiDescription: Async<List<EmojiRepresentation>> = Uninitialized,
val decimalDescription: Async<String> = Uninitialized,
val isWaitingFromOther: Boolean = false
) : MavericksState
class VerificationEmojiCodeViewModel @AssistedInject constructor(
@Assisted initialState: VerificationEmojiCodeViewState,
private val session: Session
) : VectorViewModel<VerificationEmojiCodeViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener {
init {
session.cryptoService().verificationService()
.requestEventFlow()
.onEach {
when (it) {
is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request)
is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request)
is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction)
is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction)
}
}
.launchIn(viewModelScope)
viewModelScope.launch {
refreshStateFromTx(
session.cryptoService().verificationService()
.getExistingTransaction(
otherUserId = initialState.otherUser.id,
tid = initialState.transactionId ?: ""
) as? SasVerificationTransaction
)
}
// session.cryptoService().verificationService().addListener(this)
}
// override fun onCleared() {
// session.cryptoService().verificationService().removeListener(this)
// super.onCleared()
// /*
// * Copyright 2019 New Vector Ltd
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
// package im.vector.app.features.crypto.verification.emoji
//
// import com.airbnb.mvrx.Async
// import com.airbnb.mvrx.Fail
// import com.airbnb.mvrx.Loading
// import com.airbnb.mvrx.MavericksState
// import com.airbnb.mvrx.MavericksViewModelFactory
// import com.airbnb.mvrx.Success
// import com.airbnb.mvrx.Uninitialized
// import com.airbnb.mvrx.ViewModelContext
// import dagger.assisted.Assisted
// import dagger.assisted.AssistedFactory
// import dagger.assisted.AssistedInject
// import dagger.hilt.EntryPoints
// import im.vector.app.core.di.MavericksAssistedViewModelFactory
// import im.vector.app.core.di.SingletonEntryPoint
// import im.vector.app.core.di.hiltMavericksViewModelFactory
// import im.vector.app.core.platform.EmptyAction
// import im.vector.app.core.platform.EmptyViewEvents
// import im.vector.app.core.platform.VectorViewModel
// import im.vector.app.features.crypto.verification.VerificationBottomSheet
// import kotlinx.coroutines.flow.launchIn
// import kotlinx.coroutines.flow.onEach
// import kotlinx.coroutines.launch
// 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.SasVerificationTransaction
// 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.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.util.MatrixItem
// import org.matrix.android.sdk.api.util.toMatrixItem
//
// data class VerificationEmojiCodeViewState(
// val transactionId: String?,
// val otherUser: MatrixItem,
// val supportsEmoji: Boolean = true,
// val emojiDescription: Async<List<EmojiRepresentation>> = Uninitialized,
// val decimalDescription: Async<String> = Uninitialized,
// val isWaitingFromOther: Boolean = false
// ) : MavericksState
//
// class VerificationEmojiCodeViewModel @AssistedInject constructor(
// @Assisted initialState: VerificationEmojiCodeViewState,
// private val session: Session
// ) : VectorViewModel<VerificationEmojiCodeViewState, EmptyAction, EmptyViewEvents>(initialState), VerificationService.Listener {
//
// init {
//
// session.cryptoService().verificationService()
// .requestEventFlow()
// .onEach {
// when (it) {
// is VerificationEvent.RequestAdded -> verificationRequestCreated(it.request)
// is VerificationEvent.RequestUpdated -> verificationRequestUpdated(it.request)
// is VerificationEvent.TransactionAdded -> transactionCreated(it.transaction)
// is VerificationEvent.TransactionUpdated -> transactionUpdated(it.transaction)
// }
// }
// .launchIn(viewModelScope)
//
// viewModelScope.launch {
// refreshStateFromTx(
// session.cryptoService().verificationService()
// .getExistingTransaction(
// otherUserId = initialState.otherUser.id,
// tid = initialState.transactionId ?: ""
// ) as? SasVerificationTransaction
// )
//
// }
//
// // session.cryptoService().verificationService().addListener(this)
// }
//
// // override fun onCleared() {
// // session.cryptoService().verificationService().removeListener(this)
// // 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
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package im.vector.app.features.crypto.verification.qrconfirmation
import com.airbnb.epoxy.EpoxyController
import im.vector.app.R
import im.vector.app.core.epoxy.bottomSheetDividerItem
import im.vector.app.core.resources.ColorProvider
import im.vector.app.core.resources.StringProvider
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.bottomSheetVerificationBigImageItem
import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
import im.vector.app.features.displayname.getBestName
import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
import javax.inject.Inject
class VerificationQrScannedByOtherController @Inject constructor(
private val stringProvider: StringProvider,
private val colorProvider: ColorProvider
) : EpoxyController() {
var listener: Listener? = null
private var viewState: VerificationBottomSheetViewState? = null
fun update(viewState: VerificationBottomSheetViewState) {
this.viewState = viewState
requestModelBuild()
}
override fun buildModels() {
val state = viewState ?: return
val host = this
bottomSheetVerificationNoticeItem {
id("notice")
apply {
if (state.isMe) {
notice(host.stringProvider.getString(R.string.qr_code_scanned_self_verif_notice).toEpoxyCharSequence())
} else {
val name = state.otherUserMxItem.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() }
}
}
interface Listener {
fun onUserConfirmsQrCodeScanned()
fun onUserDeniesQrCodeScanned()
}
}
// /*
// * Copyright 2020 New Vector Ltd
// *
// * Licensed under the Apache License, Version 2.0 (the "License");
// * you may not use this file except in compliance with the License.
// * You may obtain a copy of the License at
// *
// * http://www.apache.org/licenses/LICENSE-2.0
// *
// * Unless required by applicable law or agreed to in writing, software
// * distributed under the License is distributed on an "AS IS" BASIS,
// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// * See the License for the specific language governing permissions and
// * limitations under the License.
// */
//
// package im.vector.app.features.crypto.verification.qrconfirmation
//
// import com.airbnb.epoxy.EpoxyController
// import im.vector.app.R
// import im.vector.app.core.epoxy.bottomSheetDividerItem
// import im.vector.app.core.resources.ColorProvider
// import im.vector.app.core.resources.StringProvider
// 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.bottomSheetVerificationBigImageItem
// import im.vector.app.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
// import im.vector.app.features.displayname.getBestName
// import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence
// import org.matrix.android.sdk.api.session.crypto.model.RoomEncryptionTrustLevel
// import javax.inject.Inject
//
// class VerificationQrScannedByOtherController @Inject constructor(
// private val stringProvider: StringProvider,
// private val colorProvider: ColorProvider
// ) : EpoxyController() {
//
// var listener: Listener? = null
//
// private var viewState: VerificationBottomSheetViewState? = null
//
// fun update(viewState: VerificationBottomSheetViewState) {
// this.viewState = viewState
// requestModelBuild()
// }
//
// override fun buildModels() {
// val state = viewState ?: return
// val host = this
//
// bottomSheetVerificationNoticeItem {
// id("notice")
// apply {
// if (state.isMe) {
// notice(host.stringProvider.getString(R.string.qr_code_scanned_self_verif_notice).toEpoxyCharSequence())
// } else {
// val name = state.otherUserMxItem.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() }
// }
// }
//
// interface Listener {
// fun onUserConfirmsQrCodeScanned()
// fun onUserDeniesQrCodeScanned()
// }
// }

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -22,10 +22,6 @@ import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
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.withState
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.platform.VectorBaseBottomSheetDialogFragment
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.VerificationBottomSheetViewModel
import im.vector.app.features.crypto.verification.cancel.VerificationCancelFragment
import im.vector.app.features.displayname.getBestName
import im.vector.app.features.home.AvatarRenderer
import kotlinx.parcelize.Parcelize
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 kotlin.reflect.KClass

View File

@ -18,7 +18,6 @@ package im.vector.app.features.crypto.verification.user
import androidx.core.text.toSpannable
import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.Async
import com.airbnb.mvrx.Fail
import com.airbnb.mvrx.Loading
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.EmojiRepresentation
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.VerificationTxState
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.util.MatrixItem
import timber.log.Timber
import javax.inject.Inject
@ -63,6 +63,8 @@ class UserVerificationController @Inject constructor(
fun onMatchButtonTapped()
fun openCamera()
fun doVerifyBySas()
fun onUserDeniesQrCodeScanned()
fun onUserConfirmsQrCodeScanned()
}
var listener: InteractionListener? = null
@ -169,7 +171,7 @@ class UserVerificationController @Inject constructor(
}
}
is Fail -> {
//TODO
// TODO
}
}
}
@ -257,14 +259,98 @@ class UserVerificationController @Inject constructor(
}
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
if (transaction.method == VerificationMethod.SAS) {
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())
}
is VerificationTxState.SasMacReceived -> {
if(!txState.codeConfirmed) {
is SasTransactionState.SasMacReceived -> {
if (!txState.codeConfirmed) {
buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty())
} else {
// waiting
@ -274,8 +360,8 @@ class UserVerificationController @Inject constructor(
}
}
}
is VerificationTxState.Cancelled,
is VerificationTxState.Done -> {
is SasTransactionState.Cancelled,
is SasTransactionState.Done -> {
// should show request status
}
else -> {
@ -286,9 +372,6 @@ class UserVerificationController @Inject constructor(
}
}
}
} else {
// TODO (QR CODe
}
}
private fun renderCancel(cancelCode: CancelCode) {

View File

@ -140,4 +140,12 @@ class UserVerificationFragment : VectorBaseFragment<BottomSheetVerificationChild
override fun doVerifyBySas() {
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.VerificationBottomSheetViewEvents
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.forEach
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
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.session.Session
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.QRCodeVerificationState
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.VerificationEvent
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.VerificationTxState
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.getUser
@ -82,25 +80,52 @@ data class UserVerificationViewState(
}
// We need immutable objects to use properly in MvrxState
data class VerificationTransactionData(
val transactionId: String,
val state: VerificationTxState,
val method: VerificationMethod,
val otherUserId: String,
sealed class VerificationTransactionData(
open val transactionId: String,
open val otherUserId: String,
) {
data class SasTransactionData(
override val transactionId: String,
val state: SasTransactionState,
override val otherUserId: String,
val otherDeviceId: String?,
val isIncoming: Boolean,
val emojiCodeRepresentation: List<EmojiRepresentation>?
)
fun VerificationTransaction.toDataClass() : VerificationTransactionData {
return VerificationTransactionData(
) : VerificationTransactionData(transactionId, otherUserId)
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,
state = this.state,
method = this.method,
state = this.state(),
otherUserId = this.otherUserId,
otherDeviceId = this.otherUserId,
otherDeviceId = this.otherDeviceId,
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(
@ -127,8 +152,8 @@ class UserVerificationViewModel @AssistedInject constructor(
session.cryptoService().verificationService()
.requestEventFlow()
.filter {
it.transactionId == currentTransactionId
|| currentTransactionId == null && initialState.otherUserId == it.getRequest()?.otherUserId
it.transactionId == currentTransactionId ||
currentTransactionId == null && initialState.otherUserId == it.getRequest()?.otherUserId
}
.onEach {
Timber.w("VALR update event ${it.getRequest()} ")
@ -142,11 +167,20 @@ class UserVerificationViewModel @AssistedInject constructor(
}
it.getTransaction()?.let {
Timber.w("VALR state updated transaction to $it")
val dClass = it.toDataClass()
if (dClass != null) {
setState {
copy(
startedTransaction = Success(it.toDataClass()),
startedTransaction = Success(dClass),
)
}
} else {
setState {
copy(
startedTransaction = Fail(IllegalArgumentException("Unsupported Transaction")),
)
}
}
}
}
.launchIn(viewModelScope)
@ -229,8 +263,30 @@ class UserVerificationViewModel @AssistedInject constructor(
is VerificationAction.GotResultFromSsss -> {
// not applicable, only for self verification
}
VerificationAction.OtherUserDidNotScanned -> TODO()
VerificationAction.OtherUserScannedSuccessfully -> TODO()
VerificationAction.OtherUserDidNotScanned -> {
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 -> {
withState { state ->
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 -> {
setState {
copy(pendingRequest = Loading())
}
viewModelScope.launch {
// TODO if self verif we should do via DM
val roomId = session.roomService().getExistingDirectRoomWithUser(initialState.otherUserId)
?: session.roomService().createDirectRoom(initialState.otherUserId)
@ -266,14 +343,12 @@ class UserVerificationViewModel @AssistedInject constructor(
Timber.w("VALR started request is $request")
setState {
copy(
pendingRequest = Success(request),
transactionId = request.transactionId
)
}
}
}
is VerificationAction.SASDoNotMatchAction -> {

View File

@ -265,7 +265,7 @@ class HomeActivity :
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup()
is HomeActivityViewEvents.ForceVerification -> {
//TODO
// TODO
// if (it.sendRequest) {
navigator.requestSelfSessionVerification(this)
// } 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.webrtc.WebRtcCallManager
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.home.AvatarRenderer
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.SetupMode
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.home.room.detail.RoomDetailActivity
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.ViewStubRoomMemberProfileHeaderBinding
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.displayname.getBestName
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.platform.VectorBaseBottomSheetDialogFragment
import im.vector.app.databinding.BottomSheetWithFragmentsBinding
import im.vector.app.features.crypto.verification.VerificationBottomSheet
import kotlinx.parcelize.Parcelize
import kotlin.reflect.KClass
@ -50,11 +49,12 @@ class DeviceListBottomSheet :
viewModel.observeViewEvents {
when (it) {
is DeviceListBottomSheetViewEvents.Verify -> {
VerificationBottomSheet.withArgs(
// roomId = null,
otherUserId = it.userId,
transactionId = it.txID
).show(requireActivity().supportFragmentManager, "REQPOP")
// TODO selfverif
// VerificationBottomSheet.withArgs(
// // roomId = null,
// otherUserId = it.userId,
// 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.VerificationService
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.flow.flow
import timber.log.Timber
@ -126,7 +125,7 @@ class DevicesViewModel @AssistedInject constructor(
session.cryptoService().verificationService().requestEventFlow()
.onEach {
when(it) {
when (it) {
is VerificationEvent.RequestUpdated -> {
if (it.request.isFinished) {
queryRefreshDevicesList()
@ -241,7 +240,7 @@ class DevicesViewModel @AssistedInject constructor(
}
override fun transactionUpdated(tx: VerificationTransaction) {
if (tx.state == VerificationTxState.Verified) {
if (tx.isSuccessful()) {
queryRefreshDevicesList()
}
}

View File

@ -39,7 +39,6 @@ import im.vector.app.databinding.DialogBaseEditTextBinding
import im.vector.app.databinding.FragmentGenericRecyclerBinding
import im.vector.app.features.auth.ReAuthActivity
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.session.crypto.model.DeviceInfo
import javax.inject.Inject
@ -78,11 +77,12 @@ class VectorSettingsDevicesFragment :
is DevicesViewEvents.RequestReAuth -> askForReAuthentication(it)
is DevicesViewEvents.PromptRenameDevice -> displayDeviceRenameDialog(it.deviceInfo)
is DevicesViewEvents.ShowVerifyDevice -> {
VerificationBottomSheet.withArgs(
// roomId = null,
otherUserId = it.userId,
transactionId = it.transactionId ?: ""
).show(childFragmentManager, "REQPOP")
// TODO selfverif
// VerificationBottomSheet.withArgs(
// // roomId = null,
// otherUserId = it.userId,
// transactionId = it.transactionId ?: ""
// ).show(childFragmentManager, "REQPOP")
}
is DevicesViewEvents.SelfVerification -> {
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 kotlinx.coroutines.flow.launchIn
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.VerificationService
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
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) {
if (tx.state == VerificationTxState.Verified) {
if (tx.isSuccessful()) {
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.databinding.FragmentSettingsDevicesBinding
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.list.NUMBER_OF_OTHER_DEVICES_TO_RENDER
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.PromptRenameDevice -> Unit // TODO. Next PR
is DevicesViewEvent.ShowVerifyDevice -> {
VerificationBottomSheet.withArgs(
// roomId = null,
otherUserId = it.userId,
transactionId = it.transactionId ?:""
).show(childFragmentManager, "REQPOP")
// TODO selfverif
// VerificationBottomSheet.withArgs(
// // roomId = null,
// otherUserId = it.userId,
// transactionId = it.transactionId ?:""
// ).show(childFragmentManager, "REQPOP")
}
is DevicesViewEvent.SelfVerification -> {
navigator.requestSelfSessionVerification(requireActivity())