diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt index cc3b57da20..418b7ac508 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationService.kt @@ -17,6 +17,7 @@ package im.vector.matrix.android.api.session.crypto.sas import im.vector.matrix.android.api.MatrixCallback +import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest /** * https://matrix.org/docs/spec/client_server/r0.5.0#key-verification-framework @@ -54,7 +55,7 @@ interface SasVerificationService { */ fun beginKeyVerification(method: String, userId: String, deviceID: String): String? - fun requestKeyVerificationInDMs(userId: String, roomId: String, callback: MatrixCallback?) + fun requestKeyVerificationInDMs(userId: String, roomId: String, callback: MatrixCallback?) : PendingVerificationRequest fun beginKeyVerificationInDMs(method: String, transactionId: String, @@ -63,11 +64,16 @@ interface SasVerificationService { otherDeviceId: String, callback: MatrixCallback?): String? + fun readyPendingVerificationInDMs(otherUserId: String, roomId: String, transactionId: String) + // fun transactionUpdated(tx: SasVerificationTransaction) interface SasVerificationListener { fun transactionCreated(tx: SasVerificationTransaction) fun transactionUpdated(tx: SasVerificationTransaction) - fun markedAsManuallyVerified(userId: String, deviceId: String) + fun markedAsManuallyVerified(userId: String, deviceId: String) {} + + fun verificationRequestCreated(pr: PendingVerificationRequest) {} + fun verificationRequestUpdated(pr: PendingVerificationRequest) {} } } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt index 9610daf294..b98c5c0167 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/SasVerificationTransaction.kt @@ -47,4 +47,7 @@ interface SasVerificationTransaction { * both short codes do match */ fun userHasVerifiedShortCode() + + + fun shortCodeDoNotMatch() } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt index 60d333ec96..1939b1f0e0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/events/model/EventType.kt @@ -73,6 +73,7 @@ object EventType { const val KEY_VERIFICATION_MAC = "m.key.verification.mac" const val KEY_VERIFICATION_CANCEL = "m.key.verification.cancel" const val KEY_VERIFICATION_DONE = "m.key.verification.done" + const val KEY_VERIFICATION_READY = "m.key.verification.ready" // Relation Events const val REACTION = "m.reaction" diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt index ba3b5ded78..fe110b7b9c 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/RoomService.kt @@ -89,4 +89,6 @@ interface RoomService { fun getRoomIdByAlias(roomAlias: String, searchOnServer: Boolean, callback: MatrixCallback>): Cancelable + + fun getExistingDirectRoomWithUser(otherUserId: String) : Room? } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageVerificationReadyContent.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageVerificationReadyContent.kt new file mode 100644 index 0000000000..af02118d65 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/room/model/message/MessageVerificationReadyContent.kt @@ -0,0 +1,57 @@ +/* + * 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.matrix.android.api.session.room.model.message + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.api.session.events.model.RelationType +import im.vector.matrix.android.api.session.events.model.toContent +import im.vector.matrix.android.api.session.room.model.relation.RelationDefaultContent +import im.vector.matrix.android.internal.crypto.verification.MessageVerificationReadyFactory +import im.vector.matrix.android.internal.crypto.verification.VerificationInfoReady + +@JsonClass(generateAdapter = true) +internal data class MessageVerificationReadyContent( + @Json(name = "from_device") override val fromDevice: String? = null, + @Json(name = "methods") override val methods: List? = null, + @Json(name = "m.relates_to") val relatesTo: RelationDefaultContent? +) : VerificationInfoReady { + + override val transactionID: String? + get() = relatesTo?.eventId + + override fun toEventContent() = this.toContent() + + override fun isValid(): Boolean { + if (transactionID.isNullOrBlank() || methods.isNullOrEmpty() || fromDevice.isNullOrEmpty()) { + return false + } + return true + } + + companion object : MessageVerificationReadyFactory { + override fun create(tid: String, methods: List, fromDevice: String): VerificationInfoReady { + return MessageVerificationReadyContent( + fromDevice = fromDevice, + methods = methods, + relatesTo = RelationDefaultContent( + RelationType.REFERENCE, + tid + ) + ) + } + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationReady.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationReady.kt new file mode 100644 index 0000000000..7df12b22c6 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationReady.kt @@ -0,0 +1,38 @@ +/* + * 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.matrix.android.internal.crypto.model.rest + +import com.squareup.moshi.Json +import com.squareup.moshi.JsonClass +import im.vector.matrix.android.internal.crypto.verification.VerificationInfoReady + +/** + * Requests a key verification with another user's devices. + */ +@JsonClass(generateAdapter = true) +internal data class KeyVerificationReady( + @Json(name = "from_device") override val fromDevice: String?, + //TODO add qr? + @Json(name = "methods") override val methods: List? = listOf(KeyVerificationStart.VERIF_METHOD_SAS), + @Json(name = "transaction_id") override var transactionID: String? = null +) : SendToDeviceObject, VerificationInfoReady { + + override fun toSendToDeviceObject() = this + + override fun isValid(): Boolean { + return !transactionID.isNullOrBlank() && !fromDevice.isNullOrBlank() && !methods.isNullOrEmpty() + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt index e8c0334539..e25ed10a6a 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/KeyVerificationStart.kt @@ -43,6 +43,7 @@ data class KeyVerificationStart( companion object { const val VERIF_METHOD_SAS = "m.sas.v1" + const val VERIF_METHOD_SCAN = "m.qr_code.scan.v1" } override fun isValid(): Boolean { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultIncomingSASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultIncomingSASVerificationTransaction.kt index 5eff26a5bb..349d6a79ca 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultIncomingSASVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultIncomingSASVerificationTransaction.kt @@ -33,7 +33,8 @@ internal class DefaultIncomingSASVerificationTransaction( private val cryptoStore: IMXCryptoStore, deviceFingerprint: String, transactionId: String, - otherUserID: String + otherUserID: String, + val autoAccept: Boolean = false ) : SASVerificationTransaction( setDeviceVerificationAction, credentials, @@ -76,6 +77,10 @@ internal class DefaultIncomingSASVerificationTransaction( this.startReq = startReq state = SasVerificationTxState.OnStarted this.otherDeviceId = startReq.fromDevice + + if (autoAccept) { + performAccept() + } } override fun performAccept() { diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt index 050dcea9b5..64f1b4b308 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultSasVerificationService.kt @@ -75,6 +75,12 @@ internal class DefaultSasVerificationService @Inject constructor( // map [sender : [transaction]] private val txMap = HashMap>() + /** + * Map [sender: [PendingVerificationRequest]] + */ + private val pendingRequests = HashMap>() + + // Event received from the sync fun onToDeviceEvent(event: Event) { GlobalScope.launch(coroutineDispatchers.crypto) { @@ -120,6 +126,9 @@ internal class DefaultSasVerificationService @Inject constructor( EventType.KEY_VERIFICATION_MAC -> { onRoomMacReceived(event) } + EventType.KEY_VERIFICATION_READY -> { + onRoomReadyReceived(event) + } EventType.KEY_VERIFICATION_DONE -> { // TODO? } @@ -175,6 +184,31 @@ internal class DefaultSasVerificationService @Inject constructor( } } + + private fun dispatchRequestAdded(tx: PendingVerificationRequest) { + uiHandler.post { + listeners.forEach { + try { + it.verificationRequestCreated(tx) + } catch (e: Throwable) { + Timber.e(e, "## Error while notifying listeners") + } + } + } + } + + private fun dispatchRequestUpdated(tx: PendingVerificationRequest) { + uiHandler.post { + listeners.forEach { + try { + it.verificationRequestUpdated(tx) + } catch (e: Throwable) { + Timber.e(e, "## Error while notifying listeners") + } + } + } + } + override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) { setDeviceVerificationAction.handle(MXDeviceInfo.DEVICE_VERIFICATION_VERIFIED, deviceID, @@ -326,6 +360,10 @@ internal class DefaultSasVerificationService @Inject constructor( // Ok we can create if (KeyVerificationStart.VERIF_METHOD_SAS == startReq.method) { Timber.v("## SAS onStartRequestReceived - request accepted ${startReq.transactionID!!}") + // If there is a corresponding request, we can auto accept + // as we are the one requesting in first place (or we accepted the request) + val autoAccept = getExistingVerificationRequest(otherUserId)?.any { it.transactionId == startReq.transactionID } + ?: false val tx = DefaultIncomingSASVerificationTransaction( // this, setDeviceVerificationAction, @@ -333,7 +371,8 @@ internal class DefaultSasVerificationService @Inject constructor( cryptoStore, myDeviceInfoHolder.get().myDevice.fingerprint()!!, startReq.transactionID!!, - otherUserId).also { txConfigure(it) } + otherUserId, + autoAccept).also { txConfigure(it) } addTransaction(tx) tx.acceptVerificationEvent(otherUserId, startReq) } else { @@ -546,6 +585,15 @@ internal class DefaultSasVerificationService @Inject constructor( } } + private fun handleReadyReceived(senderId: String, readyReq: VerificationInfoReady) { + val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == readyReq.transactionID } + if (existingRequest == null) { + Timber.e("## SAS Received Ready for unknown request txId:${readyReq.transactionID} fromDevice ${readyReq.fromDevice}") + return + } + updateOutgoingPendingRequest(existingRequest.copy(readyInfo = readyReq)) + } + override fun getExistingTransaction(otherUser: String, tid: String): VerificationTransaction? { synchronized(lock = txMap) { return txMap[otherUser]?.get(tid) @@ -647,6 +695,12 @@ internal class DefaultSasVerificationService @Inject constructor( ) { this.callback = object : MatrixCallback { override fun onSuccess(data: SendResponse) { + params.event.getClearContent().toModel()?.let { + updateOutgoingPendingRequest(verificationRequest.copy( + transactionId = data.eventId, + requestInfo = it + )) + } callback?.onSuccess(data.eventId) } @@ -657,6 +711,24 @@ internal class DefaultSasVerificationService @Inject constructor( constraints = TaskConstraints(true) retryCount = 3 }.executeBy(taskExecutor) + + return verificationRequest + } + + private fun updateOutgoingPendingRequest(updated: PendingVerificationRequest) { + val requestsForUser = pendingRequests[updated.otherUserId] + ?: ArrayList().also { + pendingRequests[updated.otherUserId] = it + } + val index = requestsForUser.indexOfFirst { + it.transactionId == updated.transactionId + || it.transactionId == null && it.localID == updated.localID + } + if (index != -1) { + requestsForUser.removeAt(index) + } + requestsForUser.add(updated) + dispatchRequestUpdated(updated) } override fun beginKeyVerificationInDMs(method: String, transactionId: String, roomId: String, @@ -681,6 +753,27 @@ internal class DefaultSasVerificationService @Inject constructor( } } + override fun readyPendingVerificationInDMs(otherUserId: String, roomId: String, transactionId: String) { + // Let's find the related request + getExistingVerificationRequest(otherUserId)?.find { it.transactionId == transactionId }?.let { + //we need to send a ready event, with matching methods + val transport = sasTransportRoomMessageFactory.createTransport(roomId, cryptoService, null) + val methods = it.requestInfo?.methods?.intersect(listOf(KeyVerificationStart.VERIF_METHOD_SAS))?.toList() + if (methods.isNullOrEmpty()) { + Timber.i("Cannot ready this request, no common methods found txId:$transactionId") + return@let + } + //TODO this is not yet related to a transaction, maybe we should use another method like for cancel? + val readyMsg = transport.createReady(transactionId, credentials.deviceId ?: "", methods) + transport.sendToOther(EventType.KEY_VERIFICATION_READY, readyMsg, + SasVerificationTxState.None, + CancelCode.User, + null // TODO handle error? + ) + updateOutgoingPendingRequest(it.copy(readyInfo = readyMsg)) + } + } + /** * This string must be unique for the pair of users performing verification for the duration that the transaction is valid */ diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt index c0e3d292c8..9df9248993 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SASVerificationTransaction.kt @@ -169,6 +169,11 @@ internal abstract class SASVerificationTransaction( } // if not wait for it } + override fun shortCodeDoNotMatch() { + Timber.v("## SAS short code do not match for id:$transactionId") + cancel(CancelCode.MismatchedSas) + } + override fun acceptVerificationEvent(senderId: String, info: VerificationInfo) { when (info) { is VerificationInfoStart -> onVerificationStart(info) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransport.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransport.kt index ae5f55b662..1befc74525 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransport.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransport.kt @@ -58,4 +58,7 @@ internal interface SasTransport { shortAuthenticationStrings: List) : VerificationInfoStart fun createMac(tid: String, mac: Map, keys: String): VerificationInfoMac + + + fun createReady(tid: String, fromDevice: String, methods: List): VerificationInfoReady } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportRoomMessage.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportRoomMessage.kt index 91adbbd705..fa4c370a90 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportRoomMessage.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportRoomMessage.kt @@ -167,6 +167,17 @@ internal class SasTransportRoomMessage( ) ) } + + override fun createReady(tid: String, fromDevice: String, methods: List): VerificationInfoReady { + return MessageVerificationReadyContent( + fromDevice = fromDevice, + relatesTo = RelationDefaultContent( + type = RelationType.REFERENCE, + eventId = tid + ), + methods = methods + ) + } } internal class SasTransportRoomMessageFactory @Inject constructor( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportToDevice.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportToDevice.kt index 85e9099972..7a69f212a3 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportToDevice.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/SasTransportToDevice.kt @@ -126,6 +126,14 @@ internal class SasTransportToDevice( messageAuthenticationCodes, shortAuthenticationStrings) } + + override fun createReady(tid: String, fromDevice: String, methods: List): VerificationInfoReady { + return KeyVerificationReady( + transactionID = tid, + fromDevice = fromDevice, + methods = methods + ) + } } internal class SasTransportToDeviceFactory @Inject constructor( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfo.kt index 44a65aa926..5fe5c62edd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfo.kt @@ -18,7 +18,7 @@ package im.vector.matrix.android.internal.crypto.verification import im.vector.matrix.android.api.session.events.model.Content import im.vector.matrix.android.internal.crypto.model.rest.SendToDeviceObject -internal interface VerificationInfo { +interface VerificationInfo { fun toEventContent(): Content? = null fun toSendToDeviceObject(): SendToDeviceObject? = null fun isValid() : Boolean diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfoReady.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfoReady.kt new file mode 100644 index 0000000000..87436f5686 --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfoReady.kt @@ -0,0 +1,42 @@ +/* + * 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.matrix.android.internal.crypto.verification + +/** + * A new event type is added to the key verification framework: m.key.verification.ready, + * which may be sent by the target of the m.key.verification.request message, upon receipt of the m.key.verification.request event. + * + * The m.key.verification.ready event is optional; the recipient of the m.key.verification.request event may respond directly + * with a m.key.verification.start event instead. + */ +interface VerificationInfoReady : VerificationInfo { + + val transactionID: String? + + /** + * The ID of the device that sent the m.key.verification.ready message + */ + val fromDevice: String? + + /** + * An array of verification methods that the device supports + */ + val methods: List? +} + +internal interface MessageVerificationReadyFactory { + fun create(tid: String, methods: List, fromDevice: String): VerificationInfoReady +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationMessageLiveObserver.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationMessageLiveObserver.kt index 1c9849a29c..ede5c42ad6 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationMessageLiveObserver.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationMessageLiveObserver.kt @@ -49,6 +49,7 @@ internal class VerificationMessageLiveObserver @Inject constructor( EventType.KEY_VERIFICATION_MAC, EventType.KEY_VERIFICATION_CANCEL, EventType.KEY_VERIFICATION_DONE, + EventType.KEY_VERIFICATION_READY, EventType.MESSAGE, EventType.ENCRYPTED) ) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt index b53fa3ce33..dae9ba3bfd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/session/room/DefaultRoomService.kt @@ -71,6 +71,20 @@ internal class DefaultRoomService @Inject constructor(private val monarchy: Mona } } + override fun getExistingDirectRoomWithUser(otherUserId: String): Room? { + Realm.getInstance(monarchy.realmConfiguration).use { realm -> + val roomId = RoomSummaryEntity.where(realm) + .equalTo(RoomSummaryEntityFields.IS_DIRECT, true) + .findAll()?.let { dms -> + dms.firstOrNull { + it.otherMemberIds.contains(otherUserId) + } + } + ?.roomId ?: return null + return RoomEntity.where(realm, roomId).findFirst()?.let { roomFactory.create(roomId) } + } + } + override fun getRoomSummary(roomIdOrAlias: String): RoomSummary? { return monarchy .fetchCopyMap({ diff --git a/matrix-sdk-android/src/main/res/values/strings_RiotX.xml b/matrix-sdk-android/src/main/res/values/strings_RiotX.xml index 2edfd0ba03..7964b1d1b8 100644 --- a/matrix-sdk-android/src/main/res/values/strings_RiotX.xml +++ b/matrix-sdk-android/src/main/res/values/strings_RiotX.xml @@ -21,4 +21,21 @@ %s is requesting to verify your key, but your client does not support in-chat key verification. You will need to use legacy key verification to verify keys. + + You + + Verify by scanning + + Ask the other user to scan this code, or %s to scan theirs + + open your camera + + Verify by Emoji + If you can’t scan the code above, verify by comparing a short, unique selection of emoji. + + QR code image + + Verify %s + Waiting for %s… + For extra security, verify %s by checking a one-time code on both your devices.\n\nFor maximum security, do this in person. diff --git a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt index e563989368..956e7d3a65 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/FragmentModule.kt @@ -23,10 +23,7 @@ import dagger.Binds import dagger.Module import dagger.multibindings.IntoMap import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment -import im.vector.riotx.features.crypto.verification.SASVerificationIncomingFragment -import im.vector.riotx.features.crypto.verification.SASVerificationShortCodeFragment -import im.vector.riotx.features.crypto.verification.SASVerificationStartFragment -import im.vector.riotx.features.crypto.verification.SASVerificationVerifiedFragment +import im.vector.riotx.features.crypto.verification.* import im.vector.riotx.features.home.HomeDetailFragment import im.vector.riotx.features.home.HomeDrawerFragment import im.vector.riotx.features.home.LoadingFragment diff --git a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt index e0b14af9d0..3ac9e7c7e9 100644 --- a/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt +++ b/vector/src/main/java/im/vector/riotx/core/di/ScreenComponent.kt @@ -25,6 +25,7 @@ import im.vector.riotx.core.error.ErrorFormatter import im.vector.riotx.core.preference.UserAvatarPreference import im.vector.riotx.features.MainActivity import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity +import im.vector.riotx.features.crypto.verification.VerificationBottomSheet import im.vector.riotx.features.home.HomeActivity import im.vector.riotx.features.home.HomeModule import im.vector.riotx.features.home.createdirect.CreateDirectRoomActivity @@ -133,6 +134,8 @@ interface ScreenComponent { fun inject(activity: SoftLogoutActivity) + fun inject(verificationBottomSheet: VerificationBottomSheet) + fun inject(permalinkHandlerActivity: PermalinkHandlerActivity) @Component.Factory diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationCodeFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationCodeFragment.kt index 0e145c0553..1cb9d2ab94 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationCodeFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/SASVerificationCodeFragment.kt @@ -24,7 +24,6 @@ import butterknife.OnClick import com.airbnb.mvrx.* import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.core.platform.parentFragmentViewModel import kotlinx.android.synthetic.main.fragment_bottom_sas_verification_code.* import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationChooseMethodFragment.kt index bea49089eb..69abc77b6f 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationChooseMethodFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationChooseMethodFragment.kt @@ -21,10 +21,10 @@ import androidx.core.text.toSpannable import androidx.core.view.isVisible import butterknife.OnClick import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.core.platform.parentFragmentViewModel import im.vector.riotx.core.utils.tappableMatchingText import kotlinx.android.synthetic.main.fragment_verification_choose_method.* import javax.inject.Inject diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationConclusionFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationConclusionFragment.kt index 0f0eaf8b10..57bb80976e 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationConclusionFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationConclusionFragment.kt @@ -19,11 +19,11 @@ import android.os.Parcelable import androidx.core.content.ContextCompat import butterknife.OnClick import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.extensions.setTextOrHide import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.core.platform.parentFragmentViewModel import io.noties.markwon.Markwon import kotlinx.android.parcel.Parcelize import kotlinx.android.synthetic.main.fragment_verification_conclusion.* @@ -56,7 +56,9 @@ class VerificationConclusionFragment @Inject constructor() : VectorBaseFragment( verifyConclusionDescription.setTextOrHide(null) verifyConclusionImageView.setImageDrawable(ContextCompat.getDrawable(requireContext(), R.drawable.ic_shield_warning)) - verifyConclusionBottomDescription.text = Markwon.builder(requireContext()).build().toMarkdown(getString(R.string.verification_conclusion_compromised)) + verifyConclusionBottomDescription.text = Markwon.builder(requireContext()) + .build() + .toMarkdown(getString(R.string.verification_conclusion_compromised)) } ConclusionState.CANCELLED -> { // Just dismiss in this case diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationRequestFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationRequestFragment.kt index 1d499aa2ed..d6e4105fdd 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationRequestFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationRequestFragment.kt @@ -22,10 +22,10 @@ import androidx.core.view.isVisible import butterknife.OnClick import com.airbnb.mvrx.Loading import com.airbnb.mvrx.fragmentViewModel +import com.airbnb.mvrx.parentFragmentViewModel import com.airbnb.mvrx.withState import im.vector.riotx.R import im.vector.riotx.core.platform.VectorBaseFragment -import im.vector.riotx.core.platform.parentFragmentViewModel import im.vector.riotx.core.utils.colorizeMatchingText import im.vector.riotx.core.utils.styleMatchingText import im.vector.riotx.features.home.AvatarRenderer diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt index 5d00b09204..013c908f16 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailAction.kt @@ -66,4 +66,6 @@ sealed class RoomDetailAction : VectorViewModelAction { data class AcceptVerificationRequest(val transactionId: String, val otherUserId: String, val otherdDeviceId: String) : RoomDetailAction() data class DeclineVerificationRequest(val transactionId: String) : RoomDetailAction() + + data class RequestVerification(val userId: String) : RoomDetailAction() } diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt index e689f46b0a..75b24e26b4 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailFragment.kt @@ -90,6 +90,7 @@ import im.vector.riotx.features.autocomplete.group.AutocompleteGroupPresenter import im.vector.riotx.features.autocomplete.room.AutocompleteRoomPresenter import im.vector.riotx.features.autocomplete.user.AutocompleteUserPresenter import im.vector.riotx.features.command.Command +import im.vector.riotx.features.crypto.verification.VerificationBottomSheet import im.vector.riotx.features.home.AvatarRenderer import im.vector.riotx.features.home.getColorFromUserId import im.vector.riotx.features.home.room.detail.composer.TextComposerAction diff --git a/vector/src/main/res/layout/bottom_sheet_verification.xml b/vector/src/main/res/layout/bottom_sheet_verification.xml new file mode 100644 index 0000000000..7293434f0d --- /dev/null +++ b/vector/src/main/res/layout/bottom_sheet_verification.xml @@ -0,0 +1,55 @@ + + + + + + + + + + + + + + + + + + diff --git a/vector/src/main/res/layout/fragment_verification_request.xml b/vector/src/main/res/layout/fragment_verification_request.xml new file mode 100644 index 0000000000..7ae82c6a78 --- /dev/null +++ b/vector/src/main/res/layout/fragment_verification_request.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +