From 5081361c2d17fa3a46b3f455b65a5c25dff58641 Mon Sep 17 00:00:00 2001
From: Valere <valeref@matrix.org>
Date: Thu, 9 Apr 2020 16:29:52 +0200
Subject: [PATCH 1/5] Update Scan confirm flow

---
 .../verification/VerificationTxState.kt       |  1 +
 .../DefaultVerificationService.kt             | 73 +++++++++++++------
 .../DefaultVerificationTransaction.kt         | 13 +---
 .../verification/VerificationInfoDone.kt      | 10 +--
 .../DefaultQrCodeVerificationTransaction.kt   | 23 ++++--
 .../im/vector/riotx/core/di/FragmentModule.kt |  6 ++
 .../verification/VerificationBottomSheet.kt   |  8 ++
 .../VerificationQRWaitingController.kt        | 60 +++++++++++++++
 .../VerificationQRWaitingFragment.kt          | 59 +++++++++++++++
 .../VerificationQrScannedByOtherController.kt | 17 ++++-
 .../VerificationQrScannedByOtherFragment.kt   |  6 +-
 vector/src/main/res/values/strings.xml        |  2 +-
 vector/src/main/res/values/strings_riotX.xml  |  5 ++
 13 files changed, 236 insertions(+), 47 deletions(-)
 create mode 100644 vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt
 create mode 100644 vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt

diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/verification/VerificationTxState.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/verification/VerificationTxState.kt
index aaaf227187..868ec5a3e2 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/verification/VerificationTxState.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/verification/VerificationTxState.kt
@@ -43,6 +43,7 @@ sealed class VerificationTxState {
 
     // Will be used to ask the user if the other user has correctly scanned
     object QrScannedByOther : VerificationQrTxState()
+    object WaitingOtherReciprocateConfirm : VerificationQrTxState()
 
     // Terminal states
     abstract class TerminalTxState : VerificationTxState()
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt
index db6c224019..77dcc483bd 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationService.kt
@@ -646,9 +646,7 @@ internal class DefaultVerificationService @Inject constructor(
             ))
         }
 
-        if (existingTransaction is SASDefaultVerificationTransaction) {
-            existingTransaction.state = VerificationTxState.Cancelled(safeValueOf(cancelReq.code), false)
-        }
+        existingTransaction?.state = VerificationTxState.Cancelled(safeValueOf(cancelReq.code), false)
     }
 
     private fun onRoomAcceptReceived(event: Event) {
@@ -792,26 +790,53 @@ internal class DefaultVerificationService @Inject constructor(
     private fun onDoneReceived(event: Event) {
         Timber.v("## onDoneReceived")
         val doneReq = event.getClearContent().toModel<KeyVerificationDone>()?.asValidObject()
-        if (doneReq == null || event.senderId != userId) {
+        if (doneReq == null || event.senderId == null) {
             // ignore
             Timber.e("## SAS Received invalid done request")
             return
         }
 
-        // We only send gossiping request when the other sent us a done
-        // We can ask without checking too much thinks (like trust), because we will check validity of secret on reception
-        getExistingTransaction(userId, doneReq.transactionId)
-                ?: getOldTransaction(userId, doneReq.transactionId)
-                        ?.let { vt ->
-                            val otherDeviceId = vt.otherDeviceId
-                            if (!crossSigningService.canCrossSign()) {
-                                outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
-                                        ?: "*")))
-                                outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
+        handleDoneReceived(event.senderId, doneReq)
+
+        if (event.senderId == userId) {
+            // We only send gossiping request when the other sent us a done
+            // We can ask without checking too much thinks (like trust), because we will check validity of secret on reception
+            getExistingTransaction(userId, doneReq.transactionId)
+                    ?: getOldTransaction(userId, doneReq.transactionId)
+                            ?.let { vt ->
+                                val otherDeviceId = vt.otherDeviceId
+                                if (!crossSigningService.canCrossSign()) {
+                                    outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
+                                            ?: "*")))
+                                    outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
+                                            ?: "*")))
+                                }
+                                outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId
                                         ?: "*")))
                             }
-                            outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
-                        }
+        }
+    }
+
+    private fun handleDoneReceived(senderId: String, doneReq: ValidVerificationDone) {
+        Timber.v("## SAS Done receieved $doneReq")
+        val existing = getExistingTransaction(senderId, doneReq.transactionId)
+        if (existing == null) {
+            Timber.e("## SAS Received invalid Done request")
+            return
+        }
+        if (existing is DefaultQrCodeVerificationTransaction) {
+            existing.onDoneReceived()
+        } else {
+            // SAS do not care for now?
+        }
+
+        // Now transactions are udated, let's also update Requests
+        val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneReq.transactionId }
+        if (existingRequest == null) {
+            Timber.e("## SAS Received Done for unknown request txId:${doneReq.transactionId}")
+            return
+        }
+        updatePendingRequest(existingRequest.copy(isSuccessful = true))
     }
 
     private fun onRoomDoneReceived(event: Event) {
@@ -993,14 +1018,14 @@ internal class DefaultVerificationService @Inject constructor(
         )
     }
 
-    private fun handleDoneReceived(senderId: String, doneInfo: ValidVerificationDone) {
-        val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneInfo.transactionId }
-        if (existingRequest == null) {
-            Timber.e("## SAS Received Done for unknown request txId:${doneInfo.transactionId}")
-            return
-        }
-        updatePendingRequest(existingRequest.copy(isSuccessful = true))
-    }
+//    private fun handleDoneReceived(senderId: String, doneInfo: ValidVerificationDone) {
+//        val existingRequest = getExistingVerificationRequest(senderId)?.find { it.transactionId == doneInfo.transactionId }
+//        if (existingRequest == null) {
+//            Timber.e("## SAS Received Done for unknown request txId:${doneInfo.transactionId}")
+//            return
+//        }
+//        updatePendingRequest(existingRequest.copy(isSuccessful = true))
+//    }
 
     // TODO All this methods should be delegated to a TransactionStore
     override fun getExistingTransaction(otherUserId: String, tid: String): VerificationTransaction? {
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt
index 29cfcd2383..eb78aee42d 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/DefaultVerificationTransaction.kt
@@ -57,7 +57,7 @@ internal abstract class DefaultVerificationTransaction(
 
     protected fun trust(canTrustOtherUserMasterKey: Boolean,
                         toVerifyDeviceIds: List<String>,
-                        eventuallyMarkMyMasterKeyAsTrusted: Boolean) {
+                        eventuallyMarkMyMasterKeyAsTrusted: Boolean, autoDone : Boolean = true) {
         Timber.d("## Verification: trust ($otherUserId,$otherDeviceId) , verifiedDevices:$toVerifyDeviceIds")
         Timber.d("## Verification: trust Mark myMSK trusted $eventuallyMarkMyMasterKeyAsTrusted")
 
@@ -97,14 +97,9 @@ internal abstract class DefaultVerificationTransaction(
             })
         }
 
-        state = VerificationTxState.Verified
-
-        transport.done(transactionId) {
-//            if (otherUserId == userId && !crossSigningService.canCrossSign()) {
-//                outgoingGossipingRequestManager.sendSecretShareRequest(SELF_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
-//                outgoingGossipingRequestManager.sendSecretShareRequest(USER_SIGNING_KEY_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
-//                outgoingGossipingRequestManager.sendSecretShareRequest(KEYBACKUP_SECRET_SSSS_NAME, mapOf(userId to listOf(otherDeviceId ?: "*")))
-//            }
+        if (autoDone) {
+            state = VerificationTxState.Verified
+            transport.done(transactionId) {}
         }
     }
 
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfoDone.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfoDone.kt
index 2986013fca..8cf96d7d65 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfoDone.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/VerificationInfoDone.kt
@@ -15,12 +15,12 @@
  */
 package im.vector.matrix.android.internal.crypto.verification
 
-internal interface VerificationInfoDone : VerificationInfo<ValidVerificationInfoDone> {
+import im.vector.matrix.android.api.session.room.model.message.ValidVerificationDone
 
-    override fun asValidObject(): ValidVerificationInfoDone? {
+internal interface VerificationInfoDone : VerificationInfo<ValidVerificationDone> {
+
+    override fun asValidObject(): ValidVerificationDone? {
         val validTransactionId = transactionId?.takeIf { it.isNotEmpty() } ?: return null
-        return ValidVerificationInfoDone(validTransactionId)
+        return ValidVerificationDone(validTransactionId)
     }
 }
-
-internal data class ValidVerificationInfoDone(val transactionId: String)
diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt
index 41d8ce7f44..59ee23cc62 100644
--- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt
+++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/DefaultQrCodeVerificationTransaction.kt
@@ -187,9 +187,12 @@ internal class DefaultQrCodeVerificationTransaction(
         // qrCodeData.sharedSecret will be used to send the start request
         start(otherQrCodeData.sharedSecret)
 
-        trust(canTrustOtherUserMasterKey,
-                toVerifyDeviceIds.distinct(),
-                eventuallyMarkMyMasterKeyAsTrusted = true)
+        trust(
+                canTrustOtherUserMasterKey = canTrustOtherUserMasterKey,
+                toVerifyDeviceIds = toVerifyDeviceIds.distinct(),
+                eventuallyMarkMyMasterKeyAsTrusted = true,
+                autoDone = false
+        )
     }
 
     private fun start(remoteSecret: String, onDone: (() -> Unit)? = null) {
@@ -199,6 +202,7 @@ internal class DefaultQrCodeVerificationTransaction(
             throw IllegalStateException("Interactive Key verification already started")
         }
 
+        state = VerificationTxState.Started
         val startMessage = transport.createStartForQrCode(
                 deviceId,
                 transactionId,
@@ -208,7 +212,7 @@ internal class DefaultQrCodeVerificationTransaction(
         transport.sendToOther(
                 EventType.KEY_VERIFICATION_START,
                 startMessage,
-                VerificationTxState.Started,
+                VerificationTxState.WaitingOtherReciprocateConfirm,
                 CancelCode.User,
                 onDone
         )
@@ -244,6 +248,15 @@ internal class DefaultQrCodeVerificationTransaction(
         }
     }
 
+    fun onDoneReceived() {
+        if (state != VerificationTxState.WaitingOtherReciprocateConfirm) {
+            cancel(CancelCode.UnexpectedMessage)
+            return
+        }
+        state = VerificationTxState.Verified
+        transport.done(transactionId) {}
+    }
+
     override fun otherUserScannedMyQrCode() {
         when (qrCodeData) {
             is QrCodeData.VerifyingAnotherUser             -> {
@@ -265,6 +278,6 @@ internal class DefaultQrCodeVerificationTransaction(
     override fun otherUserDidNotScannedMyQrCode() {
         // What can I do then?
         // At least remove the transaction...
-        state = VerificationTxState.Cancelled(CancelCode.MismatchedKeys, true)
+        cancel(CancelCode.MismatchedKeys)
     }
 }
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 9f3cdba683..c68972cdd4 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
@@ -37,6 +37,7 @@ import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFrag
 import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
 import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
 import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment
+import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment
 import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
 import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
 import im.vector.riotx.features.grouplist.GroupListFragment
@@ -339,6 +340,11 @@ interface FragmentModule {
     @FragmentKey(VerificationQrScannedByOtherFragment::class)
     fun bindVerificationQrScannedByOtherFragment(fragment: VerificationQrScannedByOtherFragment): Fragment
 
+    @Binds
+    @IntoMap
+    @FragmentKey(VerificationQRWaitingFragment::class)
+    fun bindVerificationQRWaitingFragment(fragment: VerificationQRWaitingFragment): Fragment
+
     @Binds
     @IntoMap
     @FragmentKey(VerificationConclusionFragment::class)
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt
index 6b75c0147a..695716d386 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheet.kt
@@ -50,6 +50,7 @@ import im.vector.riotx.features.crypto.verification.cancel.VerificationNotMeFrag
 import im.vector.riotx.features.crypto.verification.choose.VerificationChooseMethodFragment
 import im.vector.riotx.features.crypto.verification.conclusion.VerificationConclusionFragment
 import im.vector.riotx.features.crypto.verification.emoji.VerificationEmojiCodeFragment
+import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQRWaitingFragment
 import im.vector.riotx.features.crypto.verification.qrconfirmation.VerificationQrScannedByOtherFragment
 import im.vector.riotx.features.crypto.verification.request.VerificationRequestFragment
 import im.vector.riotx.features.home.AvatarRenderer
@@ -244,6 +245,13 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() {
                 showFragment(VerificationQrScannedByOtherFragment::class, Bundle())
                 return@withState
             }
+            is VerificationTxState.Started,
+            is VerificationTxState.WaitingOtherReciprocateConfirm -> {
+                showFragment(VerificationQRWaitingFragment::class, Bundle().apply {
+                    putParcelable(MvRx.KEY_ARG, VerificationQRWaitingFragment.Args(state.isMe, state.otherUserMxItem?.getBestName() ?: ""))
+                })
+                return@withState
+            }
             is VerificationTxState.Verified         -> {
                 showFragment(VerificationConclusionFragment::class, Bundle().apply {
                     putParcelable(MvRx.KEY_ARG, VerificationConclusionFragment.Args(true, null, state.isMe))
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt
new file mode 100644
index 0000000000..2214774882
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingController.kt
@@ -0,0 +1,60 @@
+/*
+ * 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.riotx.features.crypto.verification.qrconfirmation
+
+import com.airbnb.epoxy.EpoxyController
+import im.vector.riotx.R
+import im.vector.riotx.core.resources.ColorProvider
+import im.vector.riotx.core.resources.StringProvider
+import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
+import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
+import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationWaitingItem
+import javax.inject.Inject
+
+class VerificationQRWaitingController @Inject constructor(
+        private val stringProvider: StringProvider,
+        private val colorProvider: ColorProvider
+) : EpoxyController() {
+
+    private var args: VerificationQRWaitingFragment.Args? = null
+
+    fun update(args: VerificationQRWaitingFragment.Args) {
+        this.args = args
+        requestModelBuild()
+    }
+
+    override fun buildModels() {
+        val params = args ?: return
+
+        bottomSheetVerificationNoticeItem {
+            id("notice")
+            apply {
+                notice(stringProvider.getString(R.string.qr_code_scanned_verif_waiting_notice))
+            }
+        }
+
+        bottomSheetVerificationBigImageItem {
+            id("image")
+            imageRes(R.drawable.ic_shield_trusted)
+        }
+
+        bottomSheetVerificationWaitingItem {
+            id("waiting")
+            title(stringProvider.getString(R.string.qr_code_scanned_verif_waiting, params.otherUserName))
+        }
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt
new file mode 100644
index 0000000000..77de3997cb
--- /dev/null
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQRWaitingFragment.kt
@@ -0,0 +1,59 @@
+/*
+ * 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.riotx.features.crypto.verification.qrconfirmation
+
+import android.os.Bundle
+import android.os.Parcelable
+import android.view.View
+import com.airbnb.mvrx.MvRx
+import im.vector.riotx.R
+import im.vector.riotx.core.extensions.cleanup
+import im.vector.riotx.core.extensions.configureWith
+import im.vector.riotx.core.platform.VectorBaseFragment
+import kotlinx.android.parcel.Parcelize
+import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
+import javax.inject.Inject
+
+class VerificationQRWaitingFragment @Inject constructor(
+        val controller: VerificationQRWaitingController
+) : VectorBaseFragment() {
+
+    @Parcelize
+    data class Args(
+            val isMe: Boolean,
+            val otherUserName: String
+    ) : Parcelable
+
+    override fun getLayoutResId() = R.layout.bottom_sheet_verification_child_fragment
+
+    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+        super.onViewCreated(view, savedInstanceState)
+        setupRecyclerView()
+        (arguments?.getParcelable(MvRx.KEY_ARG) as? Args)?.let {
+            controller.update(it)
+        }
+    }
+
+    override fun onDestroyView() {
+        bottomSheetVerificationRecyclerView.cleanup()
+        super.onDestroyView()
+    }
+
+    private fun setupRecyclerView() {
+        bottomSheetVerificationRecyclerView.configureWith(controller, hasFixedSize = false, disableItemAnimation = true)
+    }
+}
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
index f775ac7941..6271559d31 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
@@ -21,6 +21,7 @@ import im.vector.riotx.R
 import im.vector.riotx.core.epoxy.dividerItem
 import im.vector.riotx.core.resources.ColorProvider
 import im.vector.riotx.core.resources.StringProvider
+import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewState
 import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
 import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
 import javax.inject.Inject
@@ -32,14 +33,26 @@ class VerificationQrScannedByOtherController @Inject constructor(
 
     var listener: Listener? = null
 
-    init {
+    private var viewState: VerificationBottomSheetViewState? = null
+
+    fun update(viewState: VerificationBottomSheetViewState) {
+        this.viewState = viewState
         requestModelBuild()
     }
 
     override fun buildModels() {
+        val state = viewState ?: return
+
         bottomSheetVerificationNoticeItem {
             id("notice")
-            notice(stringProvider.getString(R.string.qr_code_scanned_by_other_notice))
+            apply {
+                if (state.isMe) {
+                    val name = state.otherUserMxItem?.getBestName() ?: ""
+                    notice(stringProvider.getString(R.string.qr_code_scanned_self_verif_notice, name))
+                } else {
+                    notice(stringProvider.getString(R.string.qr_code_scanned_by_other_notice))
+                }
+            }
         }
 
         dividerItem {
diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt
index 14d294a27a..a8a16f8006 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherFragment.kt
@@ -18,6 +18,7 @@ package im.vector.riotx.features.crypto.verification.qrconfirmation
 import android.os.Bundle
 import android.view.View
 import com.airbnb.mvrx.parentFragmentViewModel
+import com.airbnb.mvrx.withState
 import im.vector.riotx.R
 import im.vector.riotx.core.extensions.cleanup
 import im.vector.riotx.core.extensions.configureWith
@@ -37,10 +38,13 @@ class VerificationQrScannedByOtherFragment @Inject constructor(
 
     override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
         super.onViewCreated(view, savedInstanceState)
-
         setupRecyclerView()
     }
 
+    override fun invalidate() = withState(sharedViewModel) { state ->
+        controller.update(state)
+    }
+
     override fun onDestroyView() {
         bottomSheetVerificationRecyclerView.cleanup()
         controller.listener = null
diff --git a/vector/src/main/res/values/strings.xml b/vector/src/main/res/values/strings.xml
index 928aafbd55..ae2cb7bbbb 100644
--- a/vector/src/main/res/values/strings.xml
+++ b/vector/src/main/res/values/strings.xml
@@ -2148,7 +2148,7 @@ Not all features in Riot are implemented in RiotX yet. Main missing (and coming
 
     <string name="a11y_qr_code_for_verification">QR code</string>
 
-    <string name="qr_code_scanned_by_other_notice">Did the other user successfully scan the QR code?</string>
+    <string name="qr_code_scanned_by_other_notice">Almost there! Is %s showing the same shield?</string>
     <string name="qr_code_scanned_by_other_yes">Yes</string>
     <string name="qr_code_scanned_by_other_no">No</string>
 
diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
index a3f57b7716..9ef21170a4 100644
--- a/vector/src/main/res/values/strings_riotX.xml
+++ b/vector/src/main/res/values/strings_riotX.xml
@@ -94,6 +94,11 @@
     <string name="encryption_unknown_algorithm_tile_description">The encryption used by this room is not supported</string>
 
     <string name="room_created_summary_item">%s created and configured the room.</string>
+
+    <string name="qr_code_scanned_self_verif_notice">Almost there! Is the other device showing the same shield?</string>
+    <string name="qr_code_scanned_verif_waiting_notice">Almost there! Waiting for confirmation…</string>
+    <string name="qr_code_scanned_verif_waiting">Waiting for %s…</string>
+
     <string name="error_failed_to_import_keys">Failed to import keys</string>
 
     <!-- END Strings added by Valere -->

From 68323057aad43872952232bc02db04ad31f8c216 Mon Sep 17 00:00:00 2001
From: Valere <valeref@matrix.org>
Date: Thu, 9 Apr 2020 16:34:30 +0200
Subject: [PATCH 2/5] Update change log

---
 CHANGES.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGES.md b/CHANGES.md
index 174f4e3a6f..0df276fc2f 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -15,6 +15,7 @@ Improvements 🙌:
  - Cross-Signing | Gossip key backup recovery key (#1200)
  - Show room encryption status as a bubble tile (#1078)
  - Cross-Signing | Restore history after recover from passphrase (#1214)
+ - Cross-Sign | QR code scan confirmation screens design update (#1187)
 
 Bugfix 🐛:
  - Missing avatar/displayname after verification request message (#841)

From fccfd00949393b33b8cac2fa52a2518c7d2fa5cc Mon Sep 17 00:00:00 2001
From: Valere <valeref@matrix.org>
Date: Thu, 9 Apr 2020 17:04:24 +0200
Subject: [PATCH 3/5] Fix / design update

---
 .../VerificationQrScannedByOtherController.kt | 32 +++++++++++--------
 1 file changed, 19 insertions(+), 13 deletions(-)

diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
index 6271559d31..c7a086a423 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
@@ -23,6 +23,7 @@ import im.vector.riotx.core.resources.ColorProvider
 import im.vector.riotx.core.resources.StringProvider
 import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewState
 import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationActionItem
+import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationBigImageItem
 import im.vector.riotx.features.crypto.verification.epoxy.bottomSheetVerificationNoticeItem
 import javax.inject.Inject
 
@@ -55,23 +56,15 @@ class VerificationQrScannedByOtherController @Inject constructor(
             }
         }
 
+        bottomSheetVerificationBigImageItem {
+            id("image")
+            imageRes(R.drawable.ic_shield_trusted)
+        }
+
         dividerItem {
             id("sep0")
         }
 
-        bottomSheetVerificationActionItem {
-            id("confirm")
-            title(stringProvider.getString(R.string.qr_code_scanned_by_other_yes))
-            titleColor(colorProvider.getColor(R.color.riotx_accent))
-            iconRes(R.drawable.ic_check_on)
-            iconColor(colorProvider.getColor(R.color.riotx_accent))
-            listener { listener?.onUserConfirmsQrCodeScanned() }
-        }
-
-        dividerItem {
-            id("sep1")
-        }
-
         bottomSheetVerificationActionItem {
             id("deny")
             title(stringProvider.getString(R.string.qr_code_scanned_by_other_no))
@@ -80,6 +73,19 @@ class VerificationQrScannedByOtherController @Inject constructor(
             iconColor(colorProvider.getColor(R.color.vector_error_color))
             listener { listener?.onUserDeniesQrCodeScanned() }
         }
+
+        dividerItem {
+            id("sep1")
+        }
+
+        bottomSheetVerificationActionItem {
+            id("confirm")
+            title(stringProvider.getString(R.string.qr_code_scanned_by_other_yes))
+            titleColor(colorProvider.getColor(R.color.riotx_accent))
+            iconRes(R.drawable.ic_check_on)
+            iconColor(colorProvider.getColor(R.color.riotx_accent))
+            listener { listener?.onUserConfirmsQrCodeScanned() }
+        }
     }
 
     interface Listener {

From 943ba3bebdce0c86ff3579960c361fc9e0cc3972 Mon Sep 17 00:00:00 2001
From: Valere <valeref@matrix.org>
Date: Fri, 10 Apr 2020 13:14:12 +0200
Subject: [PATCH 4/5] Fix / string bad argument number - lint

---
 .../VerificationQrScannedByOtherController.kt               | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
index c7a086a423..dd1d3d0f90 100644
--- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
+++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/qrconfirmation/VerificationQrScannedByOtherController.kt
@@ -48,10 +48,10 @@ class VerificationQrScannedByOtherController @Inject constructor(
             id("notice")
             apply {
                 if (state.isMe) {
-                    val name = state.otherUserMxItem?.getBestName() ?: ""
-                    notice(stringProvider.getString(R.string.qr_code_scanned_self_verif_notice, name))
+                    notice(stringProvider.getString(R.string.qr_code_scanned_self_verif_notice))
                 } else {
-                    notice(stringProvider.getString(R.string.qr_code_scanned_by_other_notice))
+                    val name = state.otherUserMxItem?.getBestName() ?: ""
+                    notice(stringProvider.getString(R.string.qr_code_scanned_by_other_notice, name))
                 }
             }
         }

From 9cfb83f0d2cff846e78868c1b6908b2a1766348c Mon Sep 17 00:00:00 2001
From: Valere <valeref@matrix.org>
Date: Fri, 10 Apr 2020 14:31:03 +0200
Subject: [PATCH 5/5] Remove outdated translation

---
 vector/src/main/res/values-eu/strings.xml     | 1 -
 vector/src/main/res/values-fr/strings.xml     | 1 -
 vector/src/main/res/values-hu/strings.xml     | 1 -
 vector/src/main/res/values-it/strings.xml     | 1 -
 vector/src/main/res/values-sq/strings.xml     | 1 -
 vector/src/main/res/values-zh-rTW/strings.xml | 1 -
 6 files changed, 6 deletions(-)

diff --git a/vector/src/main/res/values-eu/strings.xml b/vector/src/main/res/values-eu/strings.xml
index 86fb1e0e12..dd8ead5f27 100644
--- a/vector/src/main/res/values-eu/strings.xml
+++ b/vector/src/main/res/values-eu/strings.xml
@@ -2166,7 +2166,6 @@ Abisua: Fitxategi hau ezabatu daiteke aplikazioa desinstalatzen bada.</string>
 
     <string name="a11y_qr_code_for_verification">QR kodea</string>
 
-    <string name="qr_code_scanned_by_other_notice">Beste erabiltzaileak QR kodea ongi eskaneatu du\?</string>
     <string name="qr_code_scanned_by_other_yes">Bai</string>
     <string name="qr_code_scanned_by_other_no">Ez</string>
 
diff --git a/vector/src/main/res/values-fr/strings.xml b/vector/src/main/res/values-fr/strings.xml
index 341a247d52..76e2a0d0a7 100644
--- a/vector/src/main/res/values-fr/strings.xml
+++ b/vector/src/main/res/values-fr/strings.xml
@@ -2174,7 +2174,6 @@ Si vous n’avez pas configuré de nouvelle méthode de récupération, un attaq
 
     <string name="a11y_qr_code_for_verification">Code QR</string>
 
-    <string name="qr_code_scanned_by_other_notice">L’autre utilisateur a-t-il bien scanné le code QR \?</string>
     <string name="qr_code_scanned_by_other_yes">Oui</string>
     <string name="qr_code_scanned_by_other_no">Non</string>
 
diff --git a/vector/src/main/res/values-hu/strings.xml b/vector/src/main/res/values-hu/strings.xml
index 64eee79075..c4dde2e079 100644
--- a/vector/src/main/res/values-hu/strings.xml
+++ b/vector/src/main/res/values-hu/strings.xml
@@ -2169,7 +2169,6 @@ Ha nem te állítottad be a visszaállítási metódust, akkor egy támadó pró
 
     <string name="a11y_qr_code_for_verification">QR kód</string>
 
-    <string name="qr_code_scanned_by_other_notice">A másik felhasználó sikeresen beolvasta a QR kódot\?</string>
     <string name="qr_code_scanned_by_other_yes">Igen</string>
     <string name="qr_code_scanned_by_other_no">Nem</string>
 
diff --git a/vector/src/main/res/values-it/strings.xml b/vector/src/main/res/values-it/strings.xml
index 4d1ac8edaf..92b235c5dd 100644
--- a/vector/src/main/res/values-it/strings.xml
+++ b/vector/src/main/res/values-it/strings.xml
@@ -2219,7 +2219,6 @@
 
     <string name="a11y_qr_code_for_verification">Codice QR</string>
 
-    <string name="qr_code_scanned_by_other_notice">L\'altro utente ha scansionato correttamente il codice QR\?</string>
     <string name="qr_code_scanned_by_other_yes">Sì</string>
     <string name="qr_code_scanned_by_other_no">No</string>
 
diff --git a/vector/src/main/res/values-sq/strings.xml b/vector/src/main/res/values-sq/strings.xml
index 7ff5253530..96e0e1201a 100644
--- a/vector/src/main/res/values-sq/strings.xml
+++ b/vector/src/main/res/values-sq/strings.xml
@@ -2129,7 +2129,6 @@ Që të garantoni se s’ju shpëton gjë, thjesht mbajeni të aktivizuar mekani
     <string name="initialize_cross_signing">Gatit <em>CrossSigning</em></string>
     <string name="reset_cross_signing">Zeroji Kyçet</string>
 
-    <string name="qr_code_scanned_by_other_notice">A e skanoi me sukses përdoruesi tjetër kodin QR\?</string>
     <string name="qr_code_scanned_by_other_no">Jo</string>
 
     <string name="no_connectivity_to_the_server_indicator">Humbi lidhja me shërbyesin</string>
diff --git a/vector/src/main/res/values-zh-rTW/strings.xml b/vector/src/main/res/values-zh-rTW/strings.xml
index 8078ca8efa..111ee26b36 100644
--- a/vector/src/main/res/values-zh-rTW/strings.xml
+++ b/vector/src/main/res/values-zh-rTW/strings.xml
@@ -2119,7 +2119,6 @@ Matrix 中的消息可見度類似于電子郵件。我們忘記您的郵件意
 
     <string name="a11y_qr_code_for_verification">QR code</string>
 
-    <string name="qr_code_scanned_by_other_notice">其他使用者是否掃苗 QR code 成功?</string>
     <string name="qr_code_scanned_by_other_yes">是</string>
     <string name="qr_code_scanned_by_other_no">否</string>