happy path qr kotlin verif
This commit is contained in:
parent
cf366f7a9c
commit
5c82bdba38
@ -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
|
||||
|
@ -561,7 +561,6 @@ class SASTest : InstrumentedTest {
|
||||
|
||||
val requestID = req.transactionId
|
||||
|
||||
|
||||
Log.v("TEST", "== requestID is $requestID")
|
||||
|
||||
testHelper.retryPeriodically {
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
}
|
@ -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 Alice’s 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,95 +698,87 @@ 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)
|
||||
}
|
||||
|
||||
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 {
|
||||
// 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}")
|
||||
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
|
||||
// Send the shared secret so that sender can trust me
|
||||
// qrCodeData.sharedSecret will be used to send the start request
|
||||
@ -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}")
|
||||
}
|
||||
|
||||
val state = existing.state
|
||||
val isCorrectState = state is VerificationTxState.Done && !state.otherDone
|
||||
when {
|
||||
existing is KotlinSasTransaction -> {
|
||||
val state = existing.state
|
||||
val isCorrectState = state is SasTransactionState.Done && !state.otherDone
|
||||
|
||||
if (isCorrectState) {
|
||||
// XXX whatabout waiting for done?
|
||||
matchingRequest.state = EVerificationState.Done
|
||||
eventFlow.emit(VerificationEvent.RequestUpdated(matchingRequest.toPendingVerificationRequest()))
|
||||
// updatePendingRequest(
|
||||
// matchingRequest.copy(
|
||||
// isSuccessful = true
|
||||
// )
|
||||
// )
|
||||
} else {
|
||||
// TODO cancel?
|
||||
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()))
|
||||
} 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 }
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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?
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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}")
|
||||
|
@ -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))
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
// return VerificationBottomSheet().apply {
|
||||
// setArguments(
|
||||
// // 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 = session.myUserId,
|
||||
// selfVerificationMode = true
|
||||
// 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
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
|
||||
// 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"
|
||||
}
|
||||
}
|
||||
|
||||
// fun View.getParentCoordinatorLayout(): CoordinatorLayout? {
|
||||
// var current = this as? View
|
||||
// while (current != null) {
|
||||
// if (current is CoordinatorLayout) return current
|
||||
// current = current.parent as? View
|
||||
// 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 {
|
||||
// // 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"
|
||||
// }
|
||||
// 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
|
||||
// // }
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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
|
||||
// )
|
||||
// )
|
||||
// }
|
||||
// }
|
||||
|
@ -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) {}
|
||||
// }
|
||||
|
@ -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)
|
||||
// }
|
||||
// }
|
||||
|
@ -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))
|
||||
// }
|
||||
// }
|
||||
|
@ -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) {}
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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)
|
||||
// }
|
||||
// }
|
||||
|
@ -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)
|
||||
// }
|
||||
|
||||
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) {
|
||||
}
|
||||
}
|
||||
//
|
||||
// // 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) {
|
||||
// }
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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)
|
||||
// }
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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()
|
||||
// }
|
||||
// }
|
||||
|
@ -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() }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
@ -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)
|
||||
// }
|
||||
//
|
||||
// }
|
||||
|
@ -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
|
||||
|
||||
|
@ -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,28 +259,100 @@ 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 -> {
|
||||
buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty())
|
||||
}
|
||||
is VerificationTxState.SasMacReceived -> {
|
||||
if(!txState.codeConfirmed) {
|
||||
buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty())
|
||||
} else {
|
||||
// waiting
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(host.stringProvider.getString(R.string.please_wait))
|
||||
}
|
||||
when (val txState = transaction.state) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
is VerificationTxState.Cancelled,
|
||||
is VerificationTxState.Done -> {
|
||||
// should show request status
|
||||
|
||||
bottomSheetVerificationBigImageItem {
|
||||
id("image")
|
||||
roomEncryptionTrustLevel(RoomEncryptionTrustLevel.Trusted)
|
||||
}
|
||||
else -> {
|
||||
|
||||
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 SasTransactionState.SasMacReceived -> {
|
||||
if (!txState.codeConfirmed) {
|
||||
buildEmojiItem(transaction.emojiCodeRepresentation.orEmpty())
|
||||
} else {
|
||||
// waiting
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
@ -286,8 +360,17 @@ class UserVerificationController @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// TODO (QR CODe
|
||||
is SasTransactionState.Cancelled,
|
||||
is SasTransactionState.Done -> {
|
||||
// should show request status
|
||||
}
|
||||
else -> {
|
||||
// waiting
|
||||
bottomSheetVerificationWaitingItem {
|
||||
id("waiting")
|
||||
title(host.stringProvider.getString(R.string.please_wait))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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,
|
||||
val otherDeviceId: String?,
|
||||
val isIncoming: Boolean,
|
||||
val emojiCodeRepresentation: List<EmojiRepresentation>?
|
||||
)
|
||||
fun VerificationTransaction.toDataClass() : VerificationTransactionData {
|
||||
return VerificationTransactionData(
|
||||
transactionId = this.transactionId,
|
||||
state = this.state,
|
||||
method = this.method,
|
||||
otherUserId = this.otherUserId,
|
||||
otherDeviceId = this.otherUserId,
|
||||
isIncoming = this.isIncoming,
|
||||
emojiCodeRepresentation = (this as? SasVerificationTransaction)?.getEmojiCodeRepresentation()
|
||||
)
|
||||
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>?
|
||||
) : 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(),
|
||||
otherUserId = this.otherUserId,
|
||||
otherDeviceId = this.otherDeviceId,
|
||||
isIncoming = this.isIncoming,
|
||||
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,10 +167,19 @@ class UserVerificationViewModel @AssistedInject constructor(
|
||||
}
|
||||
it.getTransaction()?.let {
|
||||
Timber.w("VALR state updated transaction to $it")
|
||||
setState {
|
||||
copy(
|
||||
startedTransaction = Success(it.toDataClass()),
|
||||
)
|
||||
val dClass = it.toDataClass()
|
||||
if (dClass != null) {
|
||||
setState {
|
||||
copy(
|
||||
startedTransaction = Success(dClass),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
setState {
|
||||
copy(
|
||||
startedTransaction = Fail(IllegalArgumentException("Unsupported Transaction")),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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 -> {
|
||||
|
@ -265,7 +265,7 @@ class HomeActivity :
|
||||
HomeActivityViewEvents.PromptToEnableSessionPush -> handlePromptToEnablePush()
|
||||
HomeActivityViewEvents.StartRecoverySetupFlow -> handleStartRecoverySetup()
|
||||
is HomeActivityViewEvents.ForceVerification -> {
|
||||
//TODO
|
||||
// TODO
|
||||
// if (it.sendRequest) {
|
||||
navigator.requestSelfSessionVerification(this)
|
||||
// } else {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
@ -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()
|
||||
}
|
||||
}
|
||||
|
@ -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())
|
||||
|
Loading…
x
Reference in New Issue
Block a user