From c1259161e58f798de20618be7471590bdd358699 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 Jan 2020 14:19:04 +0100 Subject: [PATCH 01/17] QRCode: generate and scan QRCodes --- vector/build.gradle | 6 +- .../riotx/features/debug/DebugMenuActivity.kt | 51 +++++++++++++ .../debug/res/layout/activity_debug_menu.xml | 13 ++++ vector/src/main/AndroidManifest.xml | 6 +- .../src/main/assets/open_source_licenses.html | 10 +++ .../im/vector/riotx/core/di/FragmentModule.kt | 35 +++++++-- .../vector/riotx/core/di/ScreenComponent.kt | 5 +- .../im/vector/riotx/core/qrcode/QrCode.kt | 47 ++++++++++++ .../features/qrcode/QrCodeScannerActivity.kt | 72 +++++++++++++++++++ .../features/qrcode/QrCodeScannerFragment.kt | 51 +++++++++++++ .../res/layout/fragment_qr_code_scanner.xml | 18 +++++ 11 files changed, 305 insertions(+), 9 deletions(-) create mode 100644 vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt create mode 100644 vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerFragment.kt create mode 100644 vector/src/main/res/layout/fragment_qr_code_scanner.xml diff --git a/vector/build.gradle b/vector/build.gradle index 14ec9f2c21..3bcb4a35b9 100644 --- a/vector/build.gradle +++ b/vector/build.gradle @@ -27,7 +27,7 @@ static def generateVersionCodeFromTimestamp() { // It's unix timestamp, minus timestamp of October 3rd 2018 (first commit date) divided by 100: It's incremented by one every 100 seconds. // plus 20_000_000 for compatibility reason with the previous way the Version Code was computed // Note that the result will be multiplied by 10 when adding the digit for the arch - return ((getGitTimestamp() - 1_538_524_800 ) / 100).toInteger() + 20_000_000 + return ((getGitTimestamp() - 1_538_524_800) / 100).toInteger() + 20_000_000 } def generateVersionCodeFromVersionName() { @@ -351,6 +351,10 @@ dependencies { implementation "androidx.emoji:emoji-appcompat:1.0.0" + // QR-code + implementation 'com.google.zxing:core:3.4.0' + implementation 'me.dm7.barcodescanner:zxing:1.9.13' + // TESTS testImplementation 'junit:junit:4.12' androidTestImplementation 'androidx.test:runner:1.2.0' diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt index db3c91d441..8d013ceb2d 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt @@ -16,6 +16,7 @@ package im.vector.riotx.features.debug +import android.app.Activity import android.app.NotificationChannel import android.app.NotificationManager import android.content.Context @@ -37,7 +38,15 @@ import im.vector.riotx.R import im.vector.riotx.core.di.ActiveSessionHolder import im.vector.riotx.core.di.ScreenComponent import im.vector.riotx.core.platform.VectorBaseActivity +import im.vector.riotx.core.qrcode.createQrCode +import im.vector.riotx.core.utils.PERMISSIONS_FOR_TAKING_PHOTO +import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA +import im.vector.riotx.core.utils.allGranted +import im.vector.riotx.core.utils.checkPermissions +import im.vector.riotx.core.utils.toast import im.vector.riotx.features.debug.sas.DebugSasEmojiActivity +import im.vector.riotx.features.qrcode.QrCodeScannerActivity +import kotlinx.android.synthetic.debug.activity_debug_menu.* import javax.inject.Inject class DebugMenuActivity : VectorBaseActivity() { @@ -51,6 +60,15 @@ class DebugMenuActivity : VectorBaseActivity() { injector.inject(this) } + override fun initUiAndData() { + renderQrCode("https://www.example.org") + } + + private fun renderQrCode(text: String) { + val qrBitmap = createQrCode(text) + debug_qr_code.setImageBitmap(qrBitmap) + } + @OnClick(R.id.debug_test_text_view_link) fun testTextViewLink() { startActivity(Intent(this, TestLinkifyActivity::class.java)) @@ -214,4 +232,37 @@ class DebugMenuActivity : VectorBaseActivity() { } }) } + + @OnClick(R.id.debug_scan_qr_code) + fun scanQRCode() { + if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) { + doScanQRCode() + } + } + + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) + + if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && allGranted(grantResults)) { + doScanQRCode() + } + } + + private fun doScanQRCode() { + QrCodeScannerActivity.startForResult(this) + } + + override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) + if (resultCode == Activity.RESULT_OK) { + when (requestCode) { + QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> { + toast("QrCode: " + QrCodeScannerActivity.getResultText(data) + " is QRCode: " + QrCodeScannerActivity.getResultIsQrCode(data)) + + // Also update the current QR Code (reverse operation) + renderQrCode(QrCodeScannerActivity.getResultText(data) ?: "") + } + } + } + } } diff --git a/vector/src/debug/res/layout/activity_debug_menu.xml b/vector/src/debug/res/layout/activity_debug_menu.xml index 5d18121f5c..52b993e223 100644 --- a/vector/src/debug/res/layout/activity_debug_menu.xml +++ b/vector/src/debug/res/layout/activity_debug_menu.xml @@ -68,6 +68,19 @@ android:layout_height="wrap_content" android:text="Initialize XSigning" /> + + + + diff --git a/vector/src/main/AndroidManifest.xml b/vector/src/main/AndroidManifest.xml index c6e4b51c44..3207ab257a 100644 --- a/vector/src/main/AndroidManifest.xml +++ b/vector/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + - + + Copyright 2017 Sergiy Kovalchuk +
  • + ZXing +
    + Copyright 2007 ZXing authors +
  • +
  • + barcodescanner +
    + Copyright (c) 2014 Dushyanth Maguluru +
  •  Apache License
    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 53bd7d169f..52c8b840e3 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
    @@ -22,32 +22,50 @@ import androidx.fragment.app.FragmentFactory
     import dagger.Binds
     import dagger.Module
     import dagger.multibindings.IntoMap
    +import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
    +import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
     import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupSettingsFragment
     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.request.VerificationRequestFragment
    +import im.vector.riotx.features.grouplist.GroupListFragment
     import im.vector.riotx.features.home.HomeDetailFragment
     import im.vector.riotx.features.home.HomeDrawerFragment
     import im.vector.riotx.features.home.LoadingFragment
    -import im.vector.riotx.features.createdirect.CreateDirectRoomDirectoryUsersFragment
    -import im.vector.riotx.features.createdirect.CreateDirectRoomKnownUsersFragment
    -import im.vector.riotx.features.grouplist.GroupListFragment
     import im.vector.riotx.features.home.room.breadcrumbs.BreadcrumbsFragment
     import im.vector.riotx.features.home.room.detail.RoomDetailFragment
     import im.vector.riotx.features.home.room.list.RoomListFragment
    -import im.vector.riotx.features.login.*
    +import im.vector.riotx.features.login.LoginCaptchaFragment
    +import im.vector.riotx.features.login.LoginFragment
    +import im.vector.riotx.features.login.LoginGenericTextInputFormFragment
    +import im.vector.riotx.features.login.LoginResetPasswordFragment
    +import im.vector.riotx.features.login.LoginResetPasswordMailConfirmationFragment
    +import im.vector.riotx.features.login.LoginResetPasswordSuccessFragment
    +import im.vector.riotx.features.login.LoginServerSelectionFragment
    +import im.vector.riotx.features.login.LoginServerUrlFormFragment
    +import im.vector.riotx.features.login.LoginSignUpSignInSelectionFragment
    +import im.vector.riotx.features.login.LoginSplashFragment
    +import im.vector.riotx.features.login.LoginWaitForEmailFragment
    +import im.vector.riotx.features.login.LoginWebFragment
     import im.vector.riotx.features.login.terms.LoginTermsFragment
    -import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment
    +import im.vector.riotx.features.qrcode.QrCodeScannerFragment
     import im.vector.riotx.features.reactions.EmojiChooserFragment
     import im.vector.riotx.features.reactions.EmojiSearchResultFragment
     import im.vector.riotx.features.roomdirectory.PublicRoomsFragment
     import im.vector.riotx.features.roomdirectory.createroom.CreateRoomFragment
     import im.vector.riotx.features.roomdirectory.picker.RoomDirectoryPickerFragment
     import im.vector.riotx.features.roomdirectory.roompreview.RoomPreviewNoPreviewFragment
    +import im.vector.riotx.features.roommemberprofile.RoomMemberProfileFragment
     import im.vector.riotx.features.roomprofile.RoomProfileFragment
     import im.vector.riotx.features.roomprofile.members.RoomMemberListFragment
    -import im.vector.riotx.features.settings.*
    +import im.vector.riotx.features.settings.VectorSettingsAdvancedNotificationPreferenceFragment
    +import im.vector.riotx.features.settings.VectorSettingsHelpAboutFragment
    +import im.vector.riotx.features.settings.VectorSettingsLabsFragment
    +import im.vector.riotx.features.settings.VectorSettingsNotificationPreferenceFragment
    +import im.vector.riotx.features.settings.VectorSettingsNotificationsTroubleshootFragment
    +import im.vector.riotx.features.settings.VectorSettingsPreferencesFragment
    +import im.vector.riotx.features.settings.VectorSettingsSecurityPrivacyFragment
     import im.vector.riotx.features.settings.devices.VectorSettingsDevicesFragment
     import im.vector.riotx.features.settings.ignored.VectorSettingsIgnoredUsersFragment
     import im.vector.riotx.features.settings.push.PushGatewaysFragment
    @@ -296,4 +314,9 @@ interface FragmentModule {
         @IntoMap
         @FragmentKey(VerificationConclusionFragment::class)
         fun bindVerificationConclusionFragment(fragment: VerificationConclusionFragment): Fragment
    +
    +    @Binds
    +    @IntoMap
    +    @FragmentKey(QrCodeScannerFragment::class)
    +    fun bindQrCodeScannerFragment(fragment: QrCodeScannerFragment): Fragment
     }
    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 4503143052..8f0b580775 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
    @@ -24,12 +24,12 @@ import dagger.Component
     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.createdirect.CreateDirectRoomActivity
     import im.vector.riotx.features.crypto.keysbackup.settings.KeysBackupManageActivity
     import im.vector.riotx.features.crypto.verification.VerificationBottomSheet
     import im.vector.riotx.features.debug.DebugMenuActivity
     import im.vector.riotx.features.home.HomeActivity
     import im.vector.riotx.features.home.HomeModule
    -import im.vector.riotx.features.createdirect.CreateDirectRoomActivity
     import im.vector.riotx.features.home.room.detail.readreceipts.DisplayReadReceiptsBottomSheet
     import im.vector.riotx.features.home.room.detail.timeline.action.MessageActionsBottomSheet
     import im.vector.riotx.features.home.room.detail.timeline.edithistory.ViewEditHistoryBottomSheet
    @@ -44,6 +44,7 @@ import im.vector.riotx.features.media.ImageMediaViewerActivity
     import im.vector.riotx.features.media.VideoMediaViewerActivity
     import im.vector.riotx.features.navigation.Navigator
     import im.vector.riotx.features.permalink.PermalinkHandlerActivity
    +import im.vector.riotx.features.qrcode.QrCodeScannerActivity
     import im.vector.riotx.features.rageshake.BugReportActivity
     import im.vector.riotx.features.rageshake.BugReporter
     import im.vector.riotx.features.rageshake.RageShake
    @@ -140,6 +141,8 @@ interface ScreenComponent {
     
         fun inject(permalinkHandlerActivity: PermalinkHandlerActivity)
     
    +    fun inject(activity: QrCodeScannerActivity)
    +
         fun inject(activity: DebugMenuActivity)
     
         @Component.Factory
    diff --git a/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt b/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt
    new file mode 100644
    index 0000000000..8610c5a8af
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt
    @@ -0,0 +1,47 @@
    +/*
    + * 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.riotx.core.qrcode
    +
    +import android.graphics.Bitmap
    +import android.graphics.Color
    +import com.google.zxing.BarcodeFormat
    +import com.google.zxing.common.BitMatrix
    +import com.google.zxing.qrcode.QRCodeWriter
    +
    +fun createQrCode(url: String,
    +                 width: Int = 200,
    +                 height: Int = 200): Bitmap {
    +    return QRCodeWriter().encode(
    +            url,
    +            BarcodeFormat.QR_CODE,
    +            width,
    +            height
    +    ).toBitmap()
    +}
    +
    +fun BitMatrix.toBitmap(): Bitmap {
    +    val height: Int = height
    +    val width: Int = width
    +    val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565)
    +    for (x in 0 until width) {
    +        for (y in 0 until height) {
    +            bmp.setPixel(x, y, if (get(x, y)) Color.BLACK else Color.WHITE)
    +        }
    +    }
    +
    +    return bmp
    +}
    diff --git a/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt b/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt
    new file mode 100644
    index 0000000000..9e0dabc45e
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerActivity.kt
    @@ -0,0 +1,72 @@
    +/*
    + * 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.riotx.features.qrcode
    +
    +import android.app.Activity
    +import android.content.Intent
    +import android.os.Bundle
    +import com.google.zxing.BarcodeFormat
    +import com.google.zxing.Result
    +import im.vector.riotx.R
    +import im.vector.riotx.core.di.ScreenComponent
    +import im.vector.riotx.core.extensions.replaceFragment
    +import im.vector.riotx.core.platform.VectorBaseActivity
    +
    +class QrCodeScannerActivity : VectorBaseActivity() {
    +
    +    override fun getLayoutRes() = R.layout.activity_simple
    +
    +    override fun injectWith(injector: ScreenComponent) {
    +        injector.inject(this)
    +    }
    +
    +    override fun onCreate(savedInstanceState: Bundle?) {
    +        super.onCreate(savedInstanceState)
    +        if (isFirstCreation()) {
    +            replaceFragment(R.id.simpleFragmentContainer, QrCodeScannerFragment::class.java)
    +        }
    +    }
    +
    +    fun setResultAndFinish(result: Result?) {
    +        result?.let {
    +            setResult(RESULT_OK, Intent().apply {
    +                putExtra(EXTRA_OUT_TEXT, it.text)
    +                putExtra(EXTRA_OUT_IS_QR_CODE, it.barcodeFormat == BarcodeFormat.QR_CODE)
    +            })
    +        }
    +        finish()
    +    }
    +
    +    companion object {
    +        private const val EXTRA_OUT_TEXT = "EXTRA_OUT_TEXT"
    +        private const val EXTRA_OUT_IS_QR_CODE = "EXTRA_OUT_IS_QR_CODE"
    +
    +        const val QR_CODE_SCANNER_REQUEST_CODE = 429
    +
    +        fun startForResult(activity: Activity, requestCode: Int = QR_CODE_SCANNER_REQUEST_CODE) {
    +            activity.startActivityForResult(Intent(activity, QrCodeScannerActivity::class.java), requestCode)
    +        }
    +
    +        fun getResultText(data: Intent?): String? {
    +            return data?.getStringExtra(EXTRA_OUT_TEXT)
    +        }
    +
    +        fun getResultIsQrCode(data: Intent?): Boolean {
    +            return data?.getBooleanExtra(EXTRA_OUT_IS_QR_CODE, false) == true
    +        }
    +    }
    +}
    diff --git a/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerFragment.kt b/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerFragment.kt
    new file mode 100644
    index 0000000000..2c6e9ed3d5
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/features/qrcode/QrCodeScannerFragment.kt
    @@ -0,0 +1,51 @@
    +/*
    + * 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.riotx.features.qrcode
    +
    +import com.google.zxing.Result
    +import im.vector.riotx.R
    +import im.vector.riotx.core.platform.VectorBaseFragment
    +import kotlinx.android.synthetic.main.fragment_qr_code_scanner.*
    +import me.dm7.barcodescanner.zxing.ZXingScannerView
    +import javax.inject.Inject
    +
    +class QrCodeScannerFragment @Inject constructor()
    +    : VectorBaseFragment(),
    +        ZXingScannerView.ResultHandler {
    +
    +    override fun getLayoutResId() = R.layout.fragment_qr_code_scanner
    +
    +    override fun onResume() {
    +        super.onResume()
    +        // Register ourselves as a handler for scan results.
    +        scannerView.setResultHandler(this)
    +        // Start camera on resume
    +        scannerView.startCamera()
    +    }
    +
    +    override fun onPause() {
    +        super.onPause()
    +        // Stop camera on pause
    +        scannerView.stopCamera()
    +    }
    +
    +    override fun handleResult(rawResult: Result?) {
    +        // Do something with the result here
    +        // This is not intended to be used outside of QrCodeScannerActivity for the moment
    +        (requireActivity() as? QrCodeScannerActivity)?.setResultAndFinish(rawResult)
    +    }
    +}
    diff --git a/vector/src/main/res/layout/fragment_qr_code_scanner.xml b/vector/src/main/res/layout/fragment_qr_code_scanner.xml
    new file mode 100644
    index 0000000000..589b7c73d4
    --- /dev/null
    +++ b/vector/src/main/res/layout/fragment_qr_code_scanner.xml
    @@ -0,0 +1,18 @@
    +
    +
    +
    +    
    +
    +    
    +
    +
    \ No newline at end of file
    
    From 537b1be0c5cc7bb29634cac4597640aec59483aa Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Wed, 22 Jan 2020 15:26:26 +0100
    Subject: [PATCH 02/17] Update wording
    
    ---
     vector/src/main/res/values/strings_riotX.xml | 2 +-
     1 file changed, 1 insertion(+), 1 deletion(-)
    
    diff --git a/vector/src/main/res/values/strings_riotX.xml b/vector/src/main/res/values/strings_riotX.xml
    index 9decb8bf8a..abf8667915 100644
    --- a/vector/src/main/res/values/strings_riotX.xml
    +++ b/vector/src/main/res/values/strings_riotX.xml
    @@ -40,7 +40,7 @@
         Can\'t scan
         If you\'re not in person, compare emoji instead
     
    -    Continue
    +    Verify by comparing emojis
     
         Verify by Emoji
         If you can’t scan the code above, verify by comparing a short, unique selection of emoji.
    
    From 79df6b840272a12e20e710cf20001b8c373e0c11 Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Wed, 22 Jan 2020 15:56:43 +0100
    Subject: [PATCH 03/17] Start plugin QR code to the code
    
    ---
     .../crypto/sas/SasVerificationTransaction.kt  |  5 +-
     .../SASVerificationTransaction.kt             |  6 ++-
     .../riotx/features/debug/DebugMenuActivity.kt |  4 +-
     .../im/vector/riotx/core/qrcode/QrCode.kt     |  7 ++-
     .../crypto/verification/VerificationAction.kt | 28 ++++++++++
     .../VerificationBottomSheetViewModel.kt       | 28 ++++++----
     .../VerificationChooseMethodController.kt     | 12 +++--
     .../VerificationChooseMethodFragment.kt       | 51 ++++++++++++++++++-
     .../VerificationChooseMethodViewModel.kt      |  5 ++
     .../BottomSheetVerificationBigImageItem.kt    | 10 +++-
     .../features/qrcode/QrCodeScannerActivity.kt  |  6 +++
     11 files changed, 138 insertions(+), 24 deletions(-)
     create mode 100644 vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt
    
    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 e4e0baa3ac..640bc501e9 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
    @@ -50,5 +50,8 @@ interface SasVerificationTransaction {
     
         fun shortCodeDoesNotMatch()
     
    -    fun isToDeviceTransport() : Boolean
    +    fun isToDeviceTransport(): Boolean
    +
    +    // TODO Not sure this is the right place to add this, because it is not Sas
    +    fun userHasScannedRemoteQrCode(scannedData: String)
     }
    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 c0dfc73129..7742d328ad 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
    @@ -204,7 +204,7 @@ internal abstract class SASVerificationTransaction(
             cancel(CancelCode.MismatchedSas)
         }
     
    -    override fun isToDeviceTransport() : Boolean {
    +    override fun isToDeviceTransport(): Boolean {
             return transport is SasTransportToDevice
         }
     
    @@ -228,6 +228,10 @@ internal abstract class SASVerificationTransaction(
     
         abstract fun onKeyVerificationMac(vKey: VerificationInfoMac)
     
    +    override fun userHasScannedRemoteQrCode(scannedData: String) {
    +        // TODO
    +    }
    +
         protected fun verifyMacs() {
             Timber.v("## SAS verifying macs for id:$transactionId")
             state = SasVerificationTxState.Verifying
    diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt
    index 8d013ceb2d..767f8cf7e1 100644
    --- a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt
    +++ b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt
    @@ -38,7 +38,7 @@ import im.vector.riotx.R
     import im.vector.riotx.core.di.ActiveSessionHolder
     import im.vector.riotx.core.di.ScreenComponent
     import im.vector.riotx.core.platform.VectorBaseActivity
    -import im.vector.riotx.core.qrcode.createQrCode
    +import im.vector.riotx.core.qrcode.toQrCode
     import im.vector.riotx.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
     import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
     import im.vector.riotx.core.utils.allGranted
    @@ -65,7 +65,7 @@ class DebugMenuActivity : VectorBaseActivity() {
         }
     
         private fun renderQrCode(text: String) {
    -        val qrBitmap = createQrCode(text)
    +        val qrBitmap = text.toQrCode()
             debug_qr_code.setImageBitmap(qrBitmap)
         }
     
    diff --git a/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt b/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt
    index 8610c5a8af..a4e7ee2e07 100644
    --- a/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt
    +++ b/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt
    @@ -22,11 +22,10 @@ import com.google.zxing.BarcodeFormat
     import com.google.zxing.common.BitMatrix
     import com.google.zxing.qrcode.QRCodeWriter
     
    -fun createQrCode(url: String,
    -                 width: Int = 200,
    -                 height: Int = 200): Bitmap {
    +fun String.toQrCode(width: Int = 200,
    +                    height: Int = 200): Bitmap {
         return QRCodeWriter().encode(
    -            url,
    +            this,
                 BarcodeFormat.QR_CODE,
                 width,
                 height
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt
    new file mode 100644
    index 0000000000..de3ea98df5
    --- /dev/null
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationAction.kt
    @@ -0,0 +1,28 @@
    +/*
    + * 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.riotx.features.crypto.verification
    +
    +import im.vector.riotx.core.platform.VectorViewModelAction
    +
    +sealed class VerificationAction : VectorViewModelAction {
    +    data class RequestVerificationByDM(val userID: String, val roomId: String?) : VerificationAction()
    +    data class StartSASVerification(val userID: String, val pendingRequestTransactionId: String) : VerificationAction()
    +    data class RemoteQrCodeScanned(val userID: String, val sasTransactionId: String, val scannedData: String) : VerificationAction()
    +    data class SASMatchAction(val userID: String, val sasTransactionId: String) : VerificationAction()
    +    data class SASDoNotMatchAction(val userID: String, val sasTransactionId: String) : VerificationAction()
    +    object GotItConclusion : VerificationAction()
    +}
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt
    index 4008207b62..5f8c33f610 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/VerificationBottomSheetViewModel.kt
    @@ -17,17 +17,25 @@ package im.vector.riotx.features.crypto.verification
     
     import androidx.lifecycle.LiveData
     import androidx.lifecycle.MutableLiveData
    -import com.airbnb.mvrx.*
    +import com.airbnb.mvrx.Async
    +import com.airbnb.mvrx.FragmentViewModelContext
    +import com.airbnb.mvrx.MvRxState
    +import com.airbnb.mvrx.MvRxViewModelFactory
    +import com.airbnb.mvrx.Success
    +import com.airbnb.mvrx.ViewModelContext
     import com.squareup.inject.assisted.Assisted
     import com.squareup.inject.assisted.AssistedInject
     import im.vector.matrix.android.api.session.Session
    -import im.vector.matrix.android.api.session.crypto.sas.*
    +import im.vector.matrix.android.api.session.crypto.sas.CancelCode
    +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationService
    +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTransaction
    +import im.vector.matrix.android.api.session.crypto.sas.SasVerificationTxState
    +import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod
     import im.vector.matrix.android.api.util.MatrixItem
     import im.vector.matrix.android.api.util.toMatrixItem
     import im.vector.matrix.android.internal.crypto.verification.PendingVerificationRequest
     import im.vector.riotx.core.di.HasScreenInjector
     import im.vector.riotx.core.platform.VectorViewModel
    -import im.vector.riotx.core.platform.VectorViewModelAction
     import im.vector.riotx.core.utils.LiveEvent
     
     data class VerificationBottomSheetViewState(
    @@ -39,14 +47,6 @@ data class VerificationBottomSheetViewState(
             val cancelCode: CancelCode? = null
     ) : MvRxState
     
    -sealed class VerificationAction : VectorViewModelAction {
    -    data class RequestVerificationByDM(val userID: String, val roomId: String?) : VerificationAction()
    -    data class StartSASVerification(val userID: String, val pendingRequestTransactionId: String) : VerificationAction()
    -    data class SASMatchAction(val userID: String, val sasTransactionId: String) : VerificationAction()
    -    data class SASDoNotMatchAction(val userID: String, val sasTransactionId: String) : VerificationAction()
    -    object GotItConclusion : VerificationAction()
    -}
    -
     class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted initialState: VerificationBottomSheetViewState,
                                                                        private val session: Session)
         : VectorViewModel(initialState),
    @@ -122,6 +122,12 @@ class VerificationBottomSheetViewModel @AssistedInject constructor(@Assisted ini
                             callback = null
                     )
                 }
    +            is VerificationAction.RemoteQrCodeScanned     -> {
    +                // TODO Use session.getCrossSigningService()?
    +                session.getSasVerificationService()
    +                        .getExistingTransaction(action.userID, action.sasTransactionId)
    +                        ?.userHasScannedRemoteQrCode(action.scannedData)
    +            }
                 is VerificationAction.SASMatchAction          -> {
                     session.getSasVerificationService()
                             .getExistingTransaction(action.userID, action.sasTransactionId)
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt
    index 8760a8603e..5cec77f368 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt
    @@ -19,8 +19,10 @@ package im.vector.riotx.features.crypto.verification.choose
     import com.airbnb.epoxy.EpoxyController
     import im.vector.riotx.R
     import im.vector.riotx.core.epoxy.dividerItem
    +import im.vector.riotx.core.qrcode.toQrCode
     import im.vector.riotx.core.resources.ColorProvider
     import im.vector.riotx.core.resources.StringProvider
    +import im.vector.riotx.core.utils.DimensionConverter
     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
    @@ -28,7 +30,8 @@ import javax.inject.Inject
     
     class VerificationChooseMethodController @Inject constructor(
             private val stringProvider: StringProvider,
    -        private val colorProvider: ColorProvider
    +        private val colorProvider: ColorProvider,
    +        private val dimensionConverter: DimensionConverter
     ) : EpoxyController() {
     
         var listener: Listener? = null
    @@ -49,10 +52,13 @@ class VerificationChooseMethodController @Inject constructor(
                     notice(stringProvider.getString(R.string.verification_scan_notice))
                 }
     
    -            // TODO Generate the QR code
    +            // Generate the QR code
    +            val size = dimensionConverter.dpToPx(180)
    +            val qrCodeBitmap = state.QRtext?.toQrCode(size, size)
    +
                 bottomSheetVerificationBigImageItem {
                     id("qr")
    -                imageRes(R.drawable.riotx_logo)
    +                imageBitmap(qrCodeBitmap)
                 }
     
                 dividerItem {
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt
    index b782afca39..110047b49c 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodFragment.kt
    @@ -15,6 +15,8 @@
      */
     package im.vector.riotx.features.crypto.verification.choose
     
    +import android.app.Activity
    +import android.content.Intent
     import android.os.Bundle
     import android.view.View
     import com.airbnb.mvrx.fragmentViewModel
    @@ -24,9 +26,15 @@ 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 im.vector.riotx.core.utils.PERMISSIONS_FOR_TAKING_PHOTO
    +import im.vector.riotx.core.utils.PERMISSION_REQUEST_CODE_LAUNCH_CAMERA
    +import im.vector.riotx.core.utils.allGranted
    +import im.vector.riotx.core.utils.checkPermissions
     import im.vector.riotx.features.crypto.verification.VerificationAction
     import im.vector.riotx.features.crypto.verification.VerificationBottomSheetViewModel
    +import im.vector.riotx.features.qrcode.QrCodeScannerActivity
     import kotlinx.android.synthetic.main.bottom_sheet_verification_child_fragment.*
    +import timber.log.Timber
     import javax.inject.Inject
     
     class VerificationChooseMethodFragment @Inject constructor(
    @@ -68,6 +76,47 @@ class VerificationChooseMethodFragment @Inject constructor(
         }
     
         override fun openCamera() {
    -        // TODO
    +        if (checkPermissions(PERMISSIONS_FOR_TAKING_PHOTO, this, PERMISSION_REQUEST_CODE_LAUNCH_CAMERA)) {
    +            doOpenQRCodeScanner()
    +        }
    +    }
    +
    +    override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
    +        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
    +
    +        if (requestCode == PERMISSION_REQUEST_CODE_LAUNCH_CAMERA && allGranted(grantResults)) {
    +            doOpenQRCodeScanner()
    +        }
    +    }
    +
    +    private fun doOpenQRCodeScanner() {
    +        QrCodeScannerActivity.startForResult(this)
    +    }
    +
    +    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    +        super.onActivityResult(requestCode, resultCode, data)
    +
    +        if (resultCode == Activity.RESULT_OK) {
    +            when (requestCode) {
    +                QrCodeScannerActivity.QR_CODE_SCANNER_REQUEST_CODE -> {
    +                    val scannedQrCode = QrCodeScannerActivity.getResultText(data)
    +                    val wasQrCode = QrCodeScannerActivity.getResultIsQrCode(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) {
    +        sharedViewModel.handle(VerificationAction.RemoteQrCodeScanned(
    +                it.otherUserMxItem?.id ?: "",
    +                it.pendingRequest?.transactionId ?: "",
    +                remoteQrCode
    +        ))
         }
     }
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    index a1d566d77b..cd28a2b709 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt
    @@ -35,6 +35,7 @@ data class VerificationChooseMethodViewState(
             val otherUserId: String = "",
             val transactionId: String = "",
             val QRModeAvailable: Boolean = false,
    +        val QRtext: String? = null,
             val SASModeAvailable: Boolean = false
     ) : MvRxState
     
    @@ -55,6 +56,8 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
             setState {
                 copy(
                         QRModeAvailable = qrAvailable,
    +                    // TODO
    +                    QRtext = "https://www.example.org",
                         SASModeAvailable = emojiAvailable
                 )
             }
    @@ -90,6 +93,8 @@ class VerificationChooseMethodViewModel @AssistedInject constructor(
                 return VerificationChooseMethodViewState(otherUserId = args.otherUserId,
                         transactionId = args.verificationId ?: "",
                         QRModeAvailable = qrAvailable,
    +                    // TODO
    +                    QRtext = "https://www.example.org",
                         SASModeAvailable = emojiAvailable
                 )
             }
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/epoxy/BottomSheetVerificationBigImageItem.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/epoxy/BottomSheetVerificationBigImageItem.kt
    index 5163f5e8a8..4973f425cd 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/epoxy/BottomSheetVerificationBigImageItem.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/epoxy/BottomSheetVerificationBigImageItem.kt
    @@ -16,6 +16,7 @@
      */
     package im.vector.riotx.features.crypto.verification.epoxy
     
    +import android.graphics.Bitmap
     import android.widget.ImageView
     import androidx.core.view.ViewCompat
     import com.airbnb.epoxy.EpoxyAttribute
    @@ -33,11 +34,18 @@ abstract class BottomSheetVerificationBigImageItem : VectorEpoxyModel
    Date: Wed, 22 Jan 2020 17:00:16 +0100
    Subject: [PATCH 04/17] Also keep the same parameter order: (userId, deviceId)
     to avoid silly errors
    
    ---
     .../crypto/keysbackup/KeysBackupTest.kt       |  2 +-
     .../api/session/crypto/CryptoService.kt       |  2 +-
     .../internal/crypto/DefaultCryptoService.kt   | 10 +++++-----
     .../actions/SetDeviceVerificationAction.kt    |  2 +-
     .../DefaultSasVerificationService.kt          |  4 ++--
     .../SASVerificationTransaction.kt             |  8 ++++----
     .../crypto/keysrequest/KeyRequestHandler.kt   | 20 +++++++++----------
     7 files changed, 24 insertions(+), 24 deletions(-)
    
    diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    index 3c77661b8b..81096424d5 100644
    --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/keysbackup/KeysBackupTest.kt
    @@ -1161,7 +1161,7 @@ class KeysBackupTest : InstrumentedTest {
             assertFalse(keysBackup2.isEnabled)
     
             // - Validate the old device from the new one
    -        aliceSession2.setDeviceVerification(DeviceTrustLevel(false, true), oldDeviceId, aliceSession2.myUserId)
    +        aliceSession2.setDeviceVerification(DeviceTrustLevel(false, true), aliceSession2.myUserId, oldDeviceId)
     
             // -> Backup should automatically enable on the new device
             val latch4 = CountDownLatch(1)
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
    index de657e6abb..2b1e92c317 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/CryptoService.kt
    @@ -59,7 +59,7 @@ interface CryptoService {
     
         fun setWarnOnUnknownDevices(warn: Boolean)
     
    -    fun setDeviceVerification(trustLevel: DeviceTrustLevel, deviceId: String, userId: String)
    +    fun setDeviceVerification(trustLevel: DeviceTrustLevel, userId: String, deviceId: String)
     
         fun getUserDevices(userId: String): MutableList
     
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    index 53b2442625..aaa712e666 100755
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/DefaultCryptoService.kt
    @@ -428,12 +428,12 @@ internal class DefaultCryptoService @Inject constructor(
         /**
          * Update the blocked/verified state of the given device.
          *
    -     * @param verificationStatus the new verification status
    -     * @param deviceId           the unique identifier for the device.
    -     * @param userId             the owner of the device
    +     * @param trustLevel the new trust level
    +     * @param userId     the owner of the device
    +     * @param deviceId   the unique identifier for the device.
          */
    -    override fun setDeviceVerification(trustLevel: DeviceTrustLevel, deviceId: String, userId: String) {
    -        setDeviceVerificationAction.handle(trustLevel, deviceId, userId)
    +    override fun setDeviceVerification(trustLevel: DeviceTrustLevel, userId: String, deviceId: String) {
    +        setDeviceVerificationAction.handle(trustLevel, userId, deviceId)
         }
     
         /**
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt
    index 6d5c6687d4..8dad832617 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/actions/SetDeviceVerificationAction.kt
    @@ -28,7 +28,7 @@ internal class SetDeviceVerificationAction @Inject constructor(
             @UserId private val userId: String,
             private val keysBackup: KeysBackup) {
     
    -    fun handle(trustLevel: DeviceTrustLevel, deviceId: String, userId: String) {
    +    fun handle(trustLevel: DeviceTrustLevel, userId: String, deviceId: String) {
             val device = cryptoStore.getUserDevice(userId, deviceId)
     
             // Sanity check
    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 faa21f9356..6e73169b28 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
    @@ -204,8 +204,8 @@ internal class DefaultSasVerificationService @Inject constructor(
     
         override fun markedLocallyAsManuallyVerified(userId: String, deviceID: String) {
             setDeviceVerificationAction.handle(DeviceTrustLevel(false, true),
    -                deviceID,
    -                userId)
    +                userId,
    +                deviceID)
     
             listeners.forEach {
                 try {
    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 7742d328ad..682c54f45f 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
    @@ -332,17 +332,17 @@ internal abstract class SASVerificationTransaction(
     
             // TODO what if the otherDevice is not in this list? and should we
             verifiedDevices.forEach {
    -            setDeviceVerified(it, otherUserId)
    +            setDeviceVerified(otherUserId, it)
             }
             transport.done(transactionId)
             state = SasVerificationTxState.Verified
         }
     
    -    private fun setDeviceVerified(deviceId: String, userId: String) {
    +    private fun setDeviceVerified(userId: String, deviceId: String) {
             // TODO should not override cross sign status
             setDeviceVerificationAction.handle(DeviceTrustLevel(false, true),
    -                deviceId,
    -                userId)
    +                userId,
    +                deviceId)
         }
     
         override fun cancel() {
    diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt
    index 0f4b651d43..28155e2d59 100644
    --- a/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt
    +++ b/vector/src/main/java/im/vector/riotx/features/crypto/keysrequest/KeyRequestHandler.kt
    @@ -89,7 +89,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
             }
     
             // Do we already have alerts for this user/device
    -        val mappingKey = keyForMap(deviceId, userId)
    +        val mappingKey = keyForMap(userId, deviceId)
             if (alertsToRequests.containsKey(mappingKey)) {
                 // just add the request, there is already an alert for this
                 alertsToRequests[mappingKey]?.add(request)
    @@ -110,7 +110,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
                     }
     
                     if (deviceInfo.isUnknown) {
    -                    session?.setDeviceVerification(DeviceTrustLevel(false, false), deviceId, userId)
    +                    session?.setDeviceVerification(DeviceTrustLevel(false, false), userId, deviceId)
     
                         deviceInfo.trustLevel = DeviceTrustLevel(false, false)
     
    @@ -181,7 +181,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
             }
     
             val alert = PopupAlertManager.VectorAlert(
    -                alertManagerId(deviceId, userId),
    +                alertManagerId(userId, deviceId),
                     context.getString(R.string.key_share_request),
                     dialogText,
                     R.drawable.key_small
    @@ -189,7 +189,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
     
             alert.colorRes = R.color.key_share_req_accent_color
     
    -        val mappingKey = keyForMap(deviceId, userId)
    +        val mappingKey = keyForMap(userId, deviceId)
             alert.dismissedAction = Runnable {
                 denyAllRequests(mappingKey)
             }
    @@ -249,7 +249,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
                 return
             }
     
    -        val alertMgrUniqueKey = alertManagerId(deviceId, userId)
    +        val alertMgrUniqueKey = alertManagerId(userId, deviceId)
             alertsToRequests[alertMgrUniqueKey]?.removeAll {
                 it.deviceId == request.deviceId
                         && it.userId == request.userId
    @@ -257,7 +257,7 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
             }
             if (alertsToRequests[alertMgrUniqueKey]?.isEmpty() == true) {
                 PopupAlertManager.cancelAlert(alertMgrUniqueKey)
    -            alertsToRequests.remove(keyForMap(deviceId, userId))
    +            alertsToRequests.remove(keyForMap(userId, deviceId))
             }
         }
     
    @@ -275,11 +275,11 @@ class KeyRequestHandler @Inject constructor(private val context: Context)
     
         override fun markedAsManuallyVerified(userId: String, deviceId: String) {
             // accept related requests
    -        shareAllSessions(keyForMap(deviceId, userId))
    -        PopupAlertManager.cancelAlert(alertManagerId(deviceId, userId))
    +        shareAllSessions(keyForMap(userId, deviceId))
    +        PopupAlertManager.cancelAlert(alertManagerId(userId, deviceId))
         }
     
    -    private fun keyForMap(deviceId: String, userId: String) = "$deviceId$userId"
    +    private fun keyForMap(userId: String, deviceId: String) = "$deviceId$userId"
     
    -    private fun alertManagerId(deviceId: String, userId: String) = "ikr_$deviceId$userId"
    +    private fun alertManagerId(userId: String, deviceId: String) = "ikr_$deviceId$userId"
     }
    
    From 41c691f26c2765671b0ae158715c380f55eab7bc Mon Sep 17 00:00:00 2001
    From: Benoit Marty 
    Date: Wed, 22 Jan 2020 17:58:25 +0100
    Subject: [PATCH 05/17] Create QrCodeData class and method to convert to URL
     and vice versa, with TUs
    
    ---
     .../api/permalinks/PermalinkFactory.kt        |  14 +-
     .../crypto/verification/qrcode/Extensions.kt  | 114 ++++++++++++++
     .../crypto/verification/qrcode/QrCodeData.kt  |  36 +++++
     .../crypto/verification/qrcode/QrCodeTest.kt  | 143 ++++++++++++++++++
     4 files changed, 305 insertions(+), 2 deletions(-)
     create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt
     create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt
     create mode 100644 matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt
    
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt
    index 1af77869ee..03c5149e6b 100644
    --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/permalinks/PermalinkFactory.kt
    @@ -19,7 +19,7 @@ package im.vector.matrix.android.api.permalinks
     import im.vector.matrix.android.api.session.events.model.Event
     
     /**
    - * Useful methods to create Matrix permalink.
    + * Useful methods to create Matrix permalink (matrix.to links).
      */
     object PermalinkFactory {
     
    @@ -84,7 +84,17 @@ object PermalinkFactory {
          * @param id the id to escape
          * @return the escaped id
          */
    -    private fun escape(id: String): String {
    +    internal fun escape(id: String): String {
             return id.replace("/", "%2F")
         }
    +
    +    /**
    +     * Unescape '/' in id
    +     *
    +     * @param id the id to escape
    +     * @return the escaped id
    +     */
    +    internal fun unescape(id: String): String {
    +        return id.replace("%2F", "/")
    +    }
     }
    diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt
    new file mode 100644
    index 0000000000..a2fc5e688c
    --- /dev/null
    +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt
    @@ -0,0 +1,114 @@
    +/*
    + * 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.matrix.android.internal.crypto.verification.qrcode
    +
    +import im.vector.matrix.android.api.MatrixPatterns
    +import im.vector.matrix.android.api.permalinks.PermalinkFactory
    +
    +/**
    + * Generate an URL to generate a QR code of the form:
    + * 
    + * https://matrix.to/#/?
    + *     request=
    + *     &action=verify
    + *     &key_=...
    + *     &verification_algorithms=
    + *     &verification_key=
    + *     &other_user_key=
    + * 
    + */ +fun QrCodeData.toUrl(): String { + return buildString { + append(PermalinkFactory.createPermalink(userId)) + append("?request=") + append(PermalinkFactory.escape(requestId)) + append("&action=verify") + + for ((keyId, key) in keys) { + append("&key_$keyId=") + append(PermalinkFactory.escape(key)) + } + + append("&verification_algorithms=") + append(PermalinkFactory.escape(verificationAlgorithms)) + append("&verification_key=") + append(PermalinkFactory.escape(verificationKey)) + append("&other_user_key=") + append(PermalinkFactory.escape(otherUserKey)) + } +} + +fun String.toQrCodeData(): QrCodeData? { + if (!startsWith(PermalinkFactory.MATRIX_TO_URL_BASE)) { + return null + } + + val fragment = substringAfter("#") + if (fragment.isEmpty()) { + return null + } + + val safeFragment = fragment.substringBefore("?") + + // we are limiting to 2 params + val params = safeFragment + .split(MatrixPatterns.SEP_REGEX.toRegex()) + .filter { it.isNotEmpty() } + + if (params.size != 1) { + return null + } + + val userId = params.getOrNull(0) + ?.let { PermalinkFactory.unescape(it) } + ?.takeIf { MatrixPatterns.isUserId(it) } ?: return null + + val urlParams = fragment.substringAfter("?") + .split("&".toRegex()) + .filter { it.isNotEmpty() } + + val keyValues = urlParams.map { + (it.substringBefore("=") to it.substringAfter("=")) + }.toMap() + + if (keyValues["action"] != "verify") { + return null + } + + val requestId = keyValues["request"] + ?.let { PermalinkFactory.unescape(it) } + ?.takeIf { MatrixPatterns.isEventId(it) } ?: return null + val verificationAlgorithms = keyValues["verification_algorithms"] ?: return null + val verificationKey = keyValues["verification_key"] ?: return null + val otherUserKey = keyValues["other_user_key"] ?: return null + + val keys = keyValues.keys + .filter { it.startsWith("key_") } + .map { + it.substringAfter("key_") to (keyValues[it] ?: return null) + } + .toMap() + + return QrCodeData( + userId, + requestId, + keys, + verificationAlgorithms, + verificationKey, + otherUserKey + ) +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt new file mode 100644 index 0000000000..9b97deb7ea --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt @@ -0,0 +1,36 @@ +/* + * 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.matrix.android.internal.crypto.verification.qrcode + +/** + * Ref: https://github.com/uhoreg/matrix-doc/blob/qr_key_verification/proposals/1543-qr_code_key_verification.md#qr-code-format + */ +data class QrCodeData( + val userId: String, + // the event ID of the associated verification request event. + val requestId: String, + // key_: each key that the user wants verified will have an entry of this form, where the value is the key in unpadded base64. + // The QR code should contain at least the user's master cross-signing key. + val keys: Map, + // algorithm + val verificationAlgorithms: String, + // random single-use shared secret in unpadded base64. It must be at least 256-bits long (43 characters when base64-encoded). + val verificationKey: String, + // the other user's master cross-signing key, in unpadded base64. In other words, if Alice is displaying the QR code, + // this would be the copy of Bob's master cross-signing key that Alice has. + val otherUserKey: String +) diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt new file mode 100644 index 0000000000..022dd76fb4 --- /dev/null +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -0,0 +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.matrix.android.internal.crypto.verification.qrcode + +import org.amshove.kluent.shouldBeEqualTo +import org.amshove.kluent.shouldBeNull +import org.amshove.kluent.shouldNotBeNull +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runners.MethodSorters + +@FixMethodOrder(MethodSorters.JVM) +class QrCodeTest { + + private val basicQrCodeData = QrCodeData( + userId = "@benoit:matrix.org", + requestId = "\$azertyazerty", + keys = mapOf( + "1" to "abcdef", + "2" to "ghijql" + ), + verificationAlgorithms = "verificationAlgorithm", + verificationKey = "verificationKey", + otherUserKey = "otherUserKey" + ) + + private val basicUrl = "https://matrix.to/#/@benoit:matrix.org?request=\$azertyazerty&action=verify&key_1=abcdef&key_2=ghijql&verification_algorithms=verificationAlgorithm&verification_key=verificationKey&other_user_key=otherUserKey" + + @Test + fun testNominalCase() { + val url = basicQrCodeData.toUrl() + + url shouldBeEqualTo basicUrl + + val decodedData = url.toQrCodeData() + + decodedData.shouldNotBeNull() + + decodedData.userId shouldBeEqualTo "@benoit:matrix.org" + decodedData.requestId shouldBeEqualTo "\$azertyazerty" + decodedData.keys["1"]?.shouldBeEqualTo("abcdef") + decodedData.keys["2"]?.shouldBeEqualTo("ghijql") + decodedData.verificationAlgorithms shouldBeEqualTo "verificationAlgorithm" + decodedData.verificationKey shouldBeEqualTo "verificationKey" + decodedData.otherUserKey shouldBeEqualTo "otherUserKey" + } + + @Test + fun testSlashCase() { + val url = basicQrCodeData + .copy( + userId = "@benoit/foo:matrix.org", + requestId = "\$azertyazerty/bar" + ) + .toUrl() + + url shouldBeEqualTo basicUrl + .replace("@benoit", "@benoit%2Ffoo") + .replace("azertyazerty", "azertyazerty%2Fbar") + + val decodedData = url.toQrCodeData() + + decodedData.shouldNotBeNull() + + decodedData.userId shouldBeEqualTo "@benoit/foo:matrix.org" + decodedData.requestId shouldBeEqualTo "\$azertyazerty/bar" + decodedData.keys["1"]?.shouldBeEqualTo("abcdef") + decodedData.keys["2"]?.shouldBeEqualTo("ghijql") + decodedData.verificationAlgorithms shouldBeEqualTo "verificationAlgorithm" + decodedData.verificationKey shouldBeEqualTo "verificationKey" + decodedData.otherUserKey shouldBeEqualTo "otherUserKey" + } + + @Test + fun testMissingActionCase() { + basicUrl.replace("&action=verify", "") + .toQrCodeData() + .shouldBeNull() + } + + @Test + fun testBadActionCase() { + basicUrl.replace("&action=verify", "&action=confirm") + .toQrCodeData() + .shouldBeNull() + } + + @Test + fun testBadRequestId() { + basicUrl.replace("\$azertyazerty", "@azertyazerty") + .toQrCodeData() + .shouldBeNull() + } + + @Test + fun testMissingUserId() { + basicUrl.replace("@benoit:matrix.org", "") + .toQrCodeData() + .shouldBeNull() + } + + @Test + fun testBadUserId() { + basicUrl.replace("@benoit:matrix.org", "@benoit") + .toQrCodeData() + .shouldBeNull() + } + + @Test + fun testMissingVerificationAlgorithm() { + basicUrl.replace("&verification_algorithms=verificationAlgorithm", "") + .toQrCodeData() + .shouldBeNull() + } + + @Test + fun testMissingVerificationKey() { + basicUrl.replace("&verification_key=verificationKey", "") + .toQrCodeData() + .shouldBeNull() + } + + @Test + fun testMissingOtherUserKey() { + basicUrl.replace("&other_user_key=otherUserKey", "") + .toQrCodeData() + .shouldBeNull() + } +} From cbf418c401145747b8d685314ba4ef369c535d76 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 Jan 2020 18:22:01 +0100 Subject: [PATCH 06/17] Update after MSC change --- .../crypto/verification/qrcode/Extensions.kt | 25 +++++------- .../crypto/verification/qrcode/QrCodeData.kt | 14 ++++--- .../crypto/verification/qrcode/QrCodeTest.kt | 38 ++++++++----------- 3 files changed, 34 insertions(+), 43 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt index a2fc5e688c..1f139343ef 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt @@ -26,8 +26,7 @@ import im.vector.matrix.android.api.permalinks.PermalinkFactory * request= * &action=verify * &key_=... - * &verification_algorithms= - * &verification_key= + * &secret= * &other_user_key= *
    */ @@ -35,18 +34,17 @@ fun QrCodeData.toUrl(): String { return buildString { append(PermalinkFactory.createPermalink(userId)) append("?request=") - append(PermalinkFactory.escape(requestId)) - append("&action=verify") + append(PermalinkFactory.escape(requestEventId)) + append("&action=") + append(action) for ((keyId, key) in keys) { append("&key_$keyId=") append(PermalinkFactory.escape(key)) } - append("&verification_algorithms=") - append(PermalinkFactory.escape(verificationAlgorithms)) - append("&verification_key=") - append(PermalinkFactory.escape(verificationKey)) + append("&secret=") + append(PermalinkFactory.escape(sharedSecret)) append("&other_user_key=") append(PermalinkFactory.escape(otherUserKey)) } @@ -85,15 +83,12 @@ fun String.toQrCodeData(): QrCodeData? { (it.substringBefore("=") to it.substringAfter("=")) }.toMap() - if (keyValues["action"] != "verify") { - return null - } + val action = keyValues["action"] ?: return null val requestId = keyValues["request"] ?.let { PermalinkFactory.unescape(it) } ?.takeIf { MatrixPatterns.isEventId(it) } ?: return null - val verificationAlgorithms = keyValues["verification_algorithms"] ?: return null - val verificationKey = keyValues["verification_key"] ?: return null + val sharedSecret = keyValues["secret"] ?: return null val otherUserKey = keyValues["other_user_key"] ?: return null val keys = keyValues.keys @@ -106,9 +101,9 @@ fun String.toQrCodeData(): QrCodeData? { return QrCodeData( userId, requestId, + action, keys, - verificationAlgorithms, - verificationKey, + sharedSecret, otherUserKey ) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt index 9b97deb7ea..8b400413b0 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeData.kt @@ -22,15 +22,19 @@ package im.vector.matrix.android.internal.crypto.verification.qrcode data class QrCodeData( val userId: String, // the event ID of the associated verification request event. - val requestId: String, + val requestEventId: String, + // The action + val action: String, // key_: each key that the user wants verified will have an entry of this form, where the value is the key in unpadded base64. // The QR code should contain at least the user's master cross-signing key. val keys: Map, - // algorithm - val verificationAlgorithms: String, // random single-use shared secret in unpadded base64. It must be at least 256-bits long (43 characters when base64-encoded). - val verificationKey: String, + val sharedSecret: String, // the other user's master cross-signing key, in unpadded base64. In other words, if Alice is displaying the QR code, // this would be the copy of Bob's master cross-signing key that Alice has. val otherUserKey: String -) +) { + companion object { + const val ACTION_VERIFY = "verify" + } +} diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt index 022dd76fb4..5eec2f3b62 100644 --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -28,17 +28,17 @@ class QrCodeTest { private val basicQrCodeData = QrCodeData( userId = "@benoit:matrix.org", - requestId = "\$azertyazerty", + requestEventId = "\$azertyazerty", + action = QrCodeData.ACTION_VERIFY, keys = mapOf( "1" to "abcdef", "2" to "ghijql" ), - verificationAlgorithms = "verificationAlgorithm", - verificationKey = "verificationKey", + sharedSecret = "sharedSecret", otherUserKey = "otherUserKey" ) - private val basicUrl = "https://matrix.to/#/@benoit:matrix.org?request=\$azertyazerty&action=verify&key_1=abcdef&key_2=ghijql&verification_algorithms=verificationAlgorithm&verification_key=verificationKey&other_user_key=otherUserKey" + private val basicUrl = "https://matrix.to/#/@benoit:matrix.org?request=\$azertyazerty&action=verify&key_1=abcdef&key_2=ghijql&secret=sharedSecret&other_user_key=otherUserKey" @Test fun testNominalCase() { @@ -51,11 +51,10 @@ class QrCodeTest { decodedData.shouldNotBeNull() decodedData.userId shouldBeEqualTo "@benoit:matrix.org" - decodedData.requestId shouldBeEqualTo "\$azertyazerty" + decodedData.requestEventId shouldBeEqualTo "\$azertyazerty" decodedData.keys["1"]?.shouldBeEqualTo("abcdef") decodedData.keys["2"]?.shouldBeEqualTo("ghijql") - decodedData.verificationAlgorithms shouldBeEqualTo "verificationAlgorithm" - decodedData.verificationKey shouldBeEqualTo "verificationKey" + decodedData.sharedSecret shouldBeEqualTo "sharedSecret" decodedData.otherUserKey shouldBeEqualTo "otherUserKey" } @@ -64,7 +63,7 @@ class QrCodeTest { val url = basicQrCodeData .copy( userId = "@benoit/foo:matrix.org", - requestId = "\$azertyazerty/bar" + requestEventId = "\$azertyazerty/bar" ) .toUrl() @@ -77,11 +76,10 @@ class QrCodeTest { decodedData.shouldNotBeNull() decodedData.userId shouldBeEqualTo "@benoit/foo:matrix.org" - decodedData.requestId shouldBeEqualTo "\$azertyazerty/bar" + decodedData.requestEventId shouldBeEqualTo "\$azertyazerty/bar" decodedData.keys["1"]?.shouldBeEqualTo("abcdef") decodedData.keys["2"]?.shouldBeEqualTo("ghijql") - decodedData.verificationAlgorithms shouldBeEqualTo "verificationAlgorithm" - decodedData.verificationKey shouldBeEqualTo "verificationKey" + decodedData.sharedSecret shouldBeEqualTo "sharedSecret" decodedData.otherUserKey shouldBeEqualTo "otherUserKey" } @@ -93,14 +91,15 @@ class QrCodeTest { } @Test - fun testBadActionCase() { + fun testOtherActionCase() { basicUrl.replace("&action=verify", "&action=confirm") .toQrCodeData() - .shouldBeNull() + ?.action + ?.shouldBeEqualTo("confirm") } @Test - fun testBadRequestId() { + fun testBadRequestEventId() { basicUrl.replace("\$azertyazerty", "@azertyazerty") .toQrCodeData() .shouldBeNull() @@ -121,15 +120,8 @@ class QrCodeTest { } @Test - fun testMissingVerificationAlgorithm() { - basicUrl.replace("&verification_algorithms=verificationAlgorithm", "") - .toQrCodeData() - .shouldBeNull() - } - - @Test - fun testMissingVerificationKey() { - basicUrl.replace("&verification_key=verificationKey", "") + fun testMissingSecret() { + basicUrl.replace("&secret=sharedSecret", "") .toQrCodeData() .shouldBeNull() } From 0e55f81879e9853ce8903b2caa418871e201657b Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 Jan 2020 18:26:34 +0100 Subject: [PATCH 07/17] Ensure all is escaped properly --- .../crypto/verification/qrcode/Extensions.kt | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt index 1f139343ef..2f90d36140 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/Extensions.kt @@ -36,10 +36,12 @@ fun QrCodeData.toUrl(): String { append("?request=") append(PermalinkFactory.escape(requestEventId)) append("&action=") - append(action) + append(PermalinkFactory.escape(action)) for ((keyId, key) in keys) { - append("&key_$keyId=") + append("&key_") + append(PermalinkFactory.escape(keyId)) + append("=") append(PermalinkFactory.escape(key)) } @@ -80,14 +82,12 @@ fun String.toQrCodeData(): QrCodeData? { .filter { it.isNotEmpty() } val keyValues = urlParams.map { - (it.substringBefore("=") to it.substringAfter("=")) + (it.substringBefore("=") to it.substringAfter("=").let { value -> PermalinkFactory.unescape(value) }) }.toMap() val action = keyValues["action"] ?: return null - val requestId = keyValues["request"] - ?.let { PermalinkFactory.unescape(it) } - ?.takeIf { MatrixPatterns.isEventId(it) } ?: return null + val requestEventId = keyValues["request"]?.takeIf { MatrixPatterns.isEventId(it) } ?: return null val sharedSecret = keyValues["secret"] ?: return null val otherUserKey = keyValues["other_user_key"] ?: return null @@ -100,7 +100,7 @@ fun String.toQrCodeData(): QrCodeData? { return QrCodeData( userId, - requestId, + requestEventId, action, keys, sharedSecret, From c323b615752931a8ffe3834f459dd348a2e09eb6 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 Jan 2020 18:27:59 +0100 Subject: [PATCH 08/17] Ignore typo --- .../android/internal/crypto/verification/qrcode/QrCodeTest.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt index 5eec2f3b62..c356ee4795 100644 --- a/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt +++ b/matrix-sdk-android/src/test/java/im/vector/matrix/android/internal/crypto/verification/qrcode/QrCodeTest.kt @@ -23,6 +23,7 @@ import org.junit.FixMethodOrder import org.junit.Test import org.junit.runners.MethodSorters +@Suppress("SpellCheckingInspection") @FixMethodOrder(MethodSorters.JVM) class QrCodeTest { From d2fab91e9dffa55fd0d0da06b4ec5089974ed0e0 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Wed, 22 Jan 2020 19:00:19 +0100 Subject: [PATCH 09/17] Improve code --- .../java/im/vector/riotx/core/qrcode/QrCode.kt | 16 ++++++++++------ .../res/layout/item_verification_big_image.xml | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt b/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt index a4e7ee2e07..0af2cf6777 100644 --- a/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt +++ b/vector/src/main/java/im/vector/riotx/core/qrcode/QrCode.kt @@ -18,27 +18,31 @@ package im.vector.riotx.core.qrcode import android.graphics.Bitmap import android.graphics.Color +import androidx.annotation.ColorInt import com.google.zxing.BarcodeFormat import com.google.zxing.common.BitMatrix import com.google.zxing.qrcode.QRCodeWriter -fun String.toQrCode(width: Int = 200, - height: Int = 200): Bitmap { +fun String.toQrCode(width: Int, + height: Int, + @ColorInt backgroundColor: Int = Color.WHITE, + @ColorInt foregroundColor: Int = Color.BLACK): Bitmap { return QRCodeWriter().encode( this, BarcodeFormat.QR_CODE, width, height - ).toBitmap() + ).toBitmap(backgroundColor, foregroundColor) } -fun BitMatrix.toBitmap(): Bitmap { +fun BitMatrix.toBitmap(@ColorInt backgroundColor: Int = Color.WHITE, + @ColorInt foregroundColor: Int = Color.BLACK): Bitmap { val height: Int = height val width: Int = width - val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565) + val bmp = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888) for (x in 0 until width) { for (y in 0 until height) { - bmp.setPixel(x, y, if (get(x, y)) Color.BLACK else Color.WHITE) + bmp.setPixel(x, y, if (get(x, y)) foregroundColor else backgroundColor) } } diff --git a/vector/src/main/res/layout/item_verification_big_image.xml b/vector/src/main/res/layout/item_verification_big_image.xml index 9f33b6c03c..e4a0db917f 100644 --- a/vector/src/main/res/layout/item_verification_big_image.xml +++ b/vector/src/main/res/layout/item_verification_big_image.xml @@ -1,7 +1,8 @@ + tools:src="@drawable/ic_shield_trusted" /> From 37b950897f2d48ff624e184501f1efa56cfc4eea Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Jan 2020 10:17:07 +0100 Subject: [PATCH 10/17] Base64 no wrap and extension for the reverse operation --- .../verification/qrcode/SharedSecretTest.kt | 46 +++++++++++ .../DefaultCrossSigningService.kt | 81 ++++++++++--------- .../crypto/crosssigning/Extensions.kt | 8 +- .../internal/crypto/store/PrivateKeysInfo.kt | 16 ++++ .../verification/qrcode/SharedSecret.kt | 29 +++++++ 5 files changed, 140 insertions(+), 40 deletions(-) create mode 100644 matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt create mode 100644 matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt new file mode 100644 index 0000000000..845d6f269a --- /dev/null +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt @@ -0,0 +1,46 @@ +/* + * 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.matrix.android.internal.crypto.verification.qrcode + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import im.vector.matrix.android.InstrumentedTest +import org.amshove.kluent.shouldBe +import org.amshove.kluent.shouldNotBeEqualTo +import org.junit.FixMethodOrder +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.MethodSorters + +@RunWith(AndroidJUnit4::class) +@FixMethodOrder(MethodSorters.JVM) +class SharedSecretTest : InstrumentedTest { + + @Test + fun testSharedSecretLengthCase() { + val sharedSecret = generateSharedSecret() + + sharedSecret.length shouldBe 43 + } + + @Test + fun testSharedDiffCase() { + val sharedSecret1 = generateSharedSecret() + val sharedSecret2 = generateSharedSecret() + + sharedSecret1 shouldNotBeEqualTo sharedSecret2 + } +} diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt index 878280295b..72a19900ba 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/DefaultCrossSigningService.kt @@ -16,7 +16,6 @@ package im.vector.matrix.android.internal.crypto.crosssigning -import android.util.Base64 import dagger.Lazy import im.vector.matrix.android.api.MatrixCallback import im.vector.matrix.android.api.auth.data.Credentials @@ -83,40 +82,43 @@ internal class DefaultCrossSigningService @Inject constructor( Timber.i("## CrossSigning - Found Existing self signed keys") Timber.i("## CrossSigning - Checking if private keys are known") - cryptoStore.getCrossSigningPrivateKeys()?.let { privateKeyinfo -> - privateKeyinfo.master?.let { privateKey -> - val keySeed = Base64.decode(privateKey, Base64.NO_PADDING) - val pkSigning = OlmPkSigning() - if (pkSigning.initWithSeed(keySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) { - masterPkSigning = pkSigning - Timber.i("## CrossSigning - Loading master key success") - } else { - Timber.w("## CrossSigning - Public master key does not match the private key") - // TODO untrust - } - } - privateKeyinfo.user?.let { privateKey -> - val keySeed = Base64.decode(privateKey, Base64.NO_PADDING) - val pkSigning = OlmPkSigning() - if (pkSigning.initWithSeed(keySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) { - userPkSigning = pkSigning - Timber.i("## CrossSigning - Loading User Signing key success") - } else { - Timber.w("## CrossSigning - Public User key does not match the private key") - // TODO untrust - } - } - privateKeyinfo.selfSigned?.let { privateKey -> - val keySeed = Base64.decode(privateKey, Base64.NO_PADDING) - val pkSigning = OlmPkSigning() - if (pkSigning.initWithSeed(keySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) { - selfSigningPkSigning = pkSigning - Timber.i("## CrossSigning - Loading Self Signing key success") - } else { - Timber.w("## CrossSigning - Public Self Signing key does not match the private key") - // TODO untrust - } - } + cryptoStore.getCrossSigningPrivateKeys()?.let { privateKeysInfo -> + privateKeysInfo.master + ?.fromBase64NoPadding() + ?.let { privateKeySeed -> + val pkSigning = OlmPkSigning() + if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.masterKey()?.unpaddedBase64PublicKey) { + masterPkSigning = pkSigning + Timber.i("## CrossSigning - Loading master key success") + } else { + Timber.w("## CrossSigning - Public master key does not match the private key") + // TODO untrust + } + } + privateKeysInfo.user + ?.fromBase64NoPadding() + ?.let { privateKeySeed -> + val pkSigning = OlmPkSigning() + if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.userKey()?.unpaddedBase64PublicKey) { + userPkSigning = pkSigning + Timber.i("## CrossSigning - Loading User Signing key success") + } else { + Timber.w("## CrossSigning - Public User key does not match the private key") + // TODO untrust + } + } + privateKeysInfo.selfSigned + ?.fromBase64NoPadding() + ?.let { privateKeySeed -> + val pkSigning = OlmPkSigning() + if (pkSigning.initWithSeed(privateKeySeed) == mxCrossSigningInfo.selfSigningKey()?.unpaddedBase64PublicKey) { + selfSigningPkSigning = pkSigning + Timber.i("## CrossSigning - Loading Self Signing key success") + } else { + Timber.w("## CrossSigning - Public Self Signing key does not match the private key") + // TODO untrust + } + } } } } catch (e: Throwable) { @@ -365,7 +367,9 @@ internal class DefaultCrossSigningService @Inject constructor( // Is the master key trusted // 1) check if I know the private key - val masterPrivateKey = cryptoStore.getCrossSigningPrivateKeys()?.master + val masterPrivateKey = cryptoStore.getCrossSigningPrivateKeys() + ?.master + ?.fromBase64NoPadding() var isMaterKeyTrusted = false if (masterPrivateKey != null) { @@ -373,11 +377,12 @@ internal class DefaultCrossSigningService @Inject constructor( var olmPkSigning: OlmPkSigning? = null try { olmPkSigning = OlmPkSigning() - val expectedPK = olmPkSigning.initWithSeed(Base64.decode(masterPrivateKey, Base64.NO_PADDING)) + val expectedPK = olmPkSigning.initWithSeed(masterPrivateKey) isMaterKeyTrusted = myMasterKey.unpaddedBase64PublicKey == expectedPK } catch (failure: Throwable) { - olmPkSigning?.releaseSigning() + Timber.e(failure) } + olmPkSigning?.releaseSigning() } else { // Maybe it's signed by a locally trusted device? myMasterKey.signatures?.get(myUserId)?.forEach { (key, value) -> diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt index 3d0f68d4f6..6ffc341881 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/crosssigning/Extensions.kt @@ -28,6 +28,10 @@ fun CryptoCrossSigningKey.canonicalSignable(): String { return JsonCanonicalizer.getCanonicalJson(Map::class.java, signalableJSONDictionary()) } -fun ByteArray.toBase64NoPadding() : String? { - return Base64.encodeToString(this, Base64.NO_PADDING) +fun ByteArray.toBase64NoPadding(): String { + return Base64.encodeToString(this, Base64.NO_PADDING or Base64.NO_WRAP) +} + +fun String.fromBase64NoPadding(): ByteArray { + return Base64.decode(this, Base64.NO_PADDING or Base64.NO_WRAP) } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/PrivateKeysInfo.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/PrivateKeysInfo.kt index e2add0beff..a10b6d2645 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/PrivateKeysInfo.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/store/PrivateKeysInfo.kt @@ -1,3 +1,19 @@ +/* + * 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.matrix.android.internal.crypto.store data class PrivateKeysInfo( diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt new file mode 100644 index 0000000000..d319ebd88c --- /dev/null +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecret.kt @@ -0,0 +1,29 @@ +/* + * 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.matrix.android.internal.crypto.verification.qrcode + +import im.vector.matrix.android.internal.crypto.crosssigning.toBase64NoPadding +import java.security.SecureRandom + +fun generateSharedSecret(): String { + val secureRandom = SecureRandom() + + // 256 bits long + val secretBytes = ByteArray(32) + secureRandom.nextBytes(secretBytes) + return secretBytes.toBase64NoPadding() +} From b3089343ad5e37818d6e4302b10771c20aeed7da Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Jan 2020 10:47:29 +0100 Subject: [PATCH 11/17] Support SCAN method (WIP) --- .../crypto/verification/qrcode/SharedSecretTest.kt | 6 +++--- .../android/api/session/crypto/sas/VerificationMethod.kt | 1 - .../internal/crypto/model/rest/VerificationMethodValues.kt | 7 +++++-- .../android/internal/crypto/verification/SasTransport.kt | 4 ++-- .../im/vector/riotx/features/crypto/verification/Config.kt | 7 +++++-- .../riotx/features/home/room/detail/RoomDetailFragment.kt | 3 ++- 6 files changed, 17 insertions(+), 11 deletions(-) diff --git a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt index 845d6f269a..7a07c16d14 100644 --- a/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt +++ b/matrix-sdk-android/src/androidTest/java/im/vector/matrix/android/internal/crypto/verification/qrcode/SharedSecretTest.kt @@ -31,9 +31,9 @@ class SharedSecretTest : InstrumentedTest { @Test fun testSharedSecretLengthCase() { - val sharedSecret = generateSharedSecret() - - sharedSecret.length shouldBe 43 + repeat(100) { + generateSharedSecret().length shouldBe 43 + } } @Test diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt index a2dd90bc84..8b65dd5645 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt @@ -21,6 +21,5 @@ package im.vector.matrix.android.api.session.crypto.sas */ enum class VerificationMethod { SAS, - // Not supported yet SCAN } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt index 168a8c8f48..04fb76bedd 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt @@ -28,5 +28,8 @@ internal fun VerificationMethod.toValue(): String { } } -// TODO Add SCAN -internal val supportedVerificationMethods = listOf(VERIFICATION_METHOD_SAS) +internal val supportedVerificationMethods = + listOf( + VERIFICATION_METHOD_SAS, + VERIFICATION_METHOD_SCAN + ) 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 12fe5c338f..2e6d5ed32a 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 @@ -37,8 +37,8 @@ internal interface SasTransport { fun sendVerificationRequest(supportedMethods: List, localID: String, otherUserId: String, - roomId: String, callback: - (String?, MessageVerificationRequestContent?) -> Unit) + roomId: String, + callback: (String?, MessageVerificationRequestContent?) -> Unit) fun cancelTransaction(transactionId: String, otherUserId: String, diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt index 0bc9a3e144..0e9011d532 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt @@ -18,5 +18,8 @@ package im.vector.riotx.features.crypto.verification import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod -// TODO Add support for SCAN (QR code) -val supportedVerificationMethods = listOf(VerificationMethod.SAS) +val supportedVerificationMethods = + listOf( + VerificationMethod.SAS, + VerificationMethod.SCAN + ) 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 236dd19030..688699c9af 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 @@ -1027,7 +1027,8 @@ class RoomDetailFragment @Inject constructor( } override fun onAvatarClicked(informationData: MessageInformationData) { - openRoomMemberProfile(informationData.senderId) + // DO NOT COMMIT openRoomMemberProfile(informationData.senderId) + roomDetailViewModel.handle(RoomDetailAction.RequestVerification(informationData.senderId)) } private fun openRoomMemberProfile(userId: String) { From 5819790c1bd976b0e961d0c0e31f0a4566692d5a Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Jan 2020 11:25:44 +0100 Subject: [PATCH 12/17] Distinguish Show SR code and Scan QR code capability --- .../session/crypto/sas/VerificationMethod.kt | 8 +++- .../model/rest/VerificationMethodValues.kt | 16 +++++-- .../DefaultSasVerificationService.kt | 1 + .../PendingVerificationRequest.kt | 10 ++-- .../riotx/features/debug/DebugMenuActivity.kt | 2 +- .../features/crypto/verification/Config.kt | 6 ++- .../VerificationChooseMethodController.kt | 46 ++++++++++--------- .../VerificationChooseMethodViewModel.kt | 17 ++++--- 8 files changed, 64 insertions(+), 42 deletions(-) diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt index 8b65dd5645..b8f0f23891 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/api/session/crypto/sas/VerificationMethod.kt @@ -17,9 +17,13 @@ package im.vector.matrix.android.api.session.crypto.sas /** - * Verification methods supported (or to be supported) by the matrix SDK + * Verification methods */ enum class VerificationMethod { + // Use it when your application supports the SAS verification method SAS, - SCAN + // Use it if your application is able to display QR codes + QR_CODE_SHOW, + // Use it if your application is able to scan QR codes + QR_CODE_SCAN } diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt index 04fb76bedd..e3659bbde2 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/model/rest/VerificationMethodValues.kt @@ -19,17 +19,25 @@ package im.vector.matrix.android.internal.crypto.model.rest import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod internal const val VERIFICATION_METHOD_SAS = "m.sas.v1" -internal const val VERIFICATION_METHOD_SCAN = "m.qr_code.scan.v1" + +// Qr code +// Ref: https://github.com/uhoreg/matrix-doc/blob/qr_key_verification/proposals/1543-qr_code_key_verification.md#verification-methods +internal const val VERIFICATION_METHOD_QR_CODE_SHOW = "m.qr_code.show.v1" +internal const val VERIFICATION_METHOD_QR_CODE_SCAN = "m.qr_code.scan.v1" +internal const val VERIFICATION_METHOD_RECIPROCATE = "m.reciprocate.v1" internal fun VerificationMethod.toValue(): String { return when (this) { - VerificationMethod.SAS -> VERIFICATION_METHOD_SAS - VerificationMethod.SCAN -> VERIFICATION_METHOD_SCAN + VerificationMethod.SAS -> VERIFICATION_METHOD_SAS + VerificationMethod.QR_CODE_SCAN -> VERIFICATION_METHOD_QR_CODE_SCAN + VerificationMethod.QR_CODE_SHOW -> VERIFICATION_METHOD_QR_CODE_SHOW } } internal val supportedVerificationMethods = listOf( VERIFICATION_METHOD_SAS, - VERIFICATION_METHOD_SCAN + VERIFICATION_METHOD_QR_CODE_SHOW, + VERIFICATION_METHOD_QR_CODE_SCAN, + VERIFICATION_METHOD_RECIPROCATE ) 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 6e73169b28..1b909c11aa 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 @@ -819,6 +819,7 @@ internal class DefaultSasVerificationService @Inject constructor( if (existingRequest != null) { // we need to send a ready event, with matching methods val transport = sasTransportRoomMessageFactory.createTransport(roomId, null) + // TODO We should not use supportedVerificationMethods here, because it depends on the client implementation val methods = existingRequest.requestInfo?.methods?.intersect(supportedVerificationMethods)?.toList() if (methods.isNullOrEmpty()) { Timber.i("Cannot ready this request, no common methods found txId:$transactionId") diff --git a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt index 0ac3847a53..531d2c8fa1 100644 --- a/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt +++ b/matrix-sdk-android/src/main/java/im/vector/matrix/android/internal/crypto/verification/PendingVerificationRequest.kt @@ -18,9 +18,10 @@ package im.vector.matrix.android.internal.crypto.verification import im.vector.matrix.android.api.session.crypto.sas.CancelCode import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod import im.vector.matrix.android.api.session.room.model.message.MessageVerificationRequestContent +import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SCAN +import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_QR_CODE_SHOW import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SAS -import im.vector.matrix.android.internal.crypto.model.rest.VERIFICATION_METHOD_SCAN -import java.util.* +import java.util.UUID /** * Stores current pending verification requests @@ -46,8 +47,9 @@ data class PendingVerificationRequest( fun hasMethod(method: VerificationMethod): Boolean? { return when (method) { - VerificationMethod.SAS -> readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS) - VerificationMethod.SCAN -> readyInfo?.methods?.contains(VERIFICATION_METHOD_SCAN) + VerificationMethod.SAS -> readyInfo?.methods?.contains(VERIFICATION_METHOD_SAS) + VerificationMethod.QR_CODE_SHOW -> readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SHOW) + VerificationMethod.QR_CODE_SCAN -> readyInfo?.methods?.contains(VERIFICATION_METHOD_QR_CODE_SCAN) } } } diff --git a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt index 767f8cf7e1..96a6f4bb2d 100644 --- a/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt +++ b/vector/src/debug/java/im/vector/riotx/features/debug/DebugMenuActivity.kt @@ -65,7 +65,7 @@ class DebugMenuActivity : VectorBaseActivity() { } private fun renderQrCode(text: String) { - val qrBitmap = text.toQrCode() + val qrBitmap = text.toQrCode(200, 200) debug_qr_code.setImageBitmap(qrBitmap) } diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt index 0e9011d532..fae7037403 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/Config.kt @@ -20,6 +20,10 @@ import im.vector.matrix.android.api.session.crypto.sas.VerificationMethod val supportedVerificationMethods = listOf( + // RiotX supports SAS verification VerificationMethod.SAS, - VerificationMethod.SCAN + // RiotX is able to show QR codes + VerificationMethod.QR_CODE_SHOW, + // RiotX is able to scan QR codes + VerificationMethod.QR_CODE_SCAN ) diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt index 5cec77f368..0bf38979da 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodController.kt @@ -46,36 +46,40 @@ class VerificationChooseMethodController @Inject constructor( override fun buildModels() { val state = viewState ?: return - if (state.QRModeAvailable) { + if (state.otherCanScanQrCode || state.otherCanShowQrCode) { bottomSheetVerificationNoticeItem { id("notice") notice(stringProvider.getString(R.string.verification_scan_notice)) } - // Generate the QR code - val size = dimensionConverter.dpToPx(180) - val qrCodeBitmap = state.QRtext?.toQrCode(size, size) + if (state.otherCanScanQrCode && !state.QRtext.isNullOrBlank()) { + // Generate the QR code + val size = dimensionConverter.dpToPx(180) + val qrCodeBitmap = state.QRtext.toQrCode(size, size) - bottomSheetVerificationBigImageItem { - id("qr") - imageBitmap(qrCodeBitmap) + bottomSheetVerificationBigImageItem { + id("qr") + imageBitmap(qrCodeBitmap) + } + + dividerItem { + id("sep0") + } } - dividerItem { - id("sep0") - } + if (state.otherCanShowQrCode) { + bottomSheetVerificationActionItem { + id("openCamera") + title(stringProvider.getString(R.string.verification_scan_their_code)) + titleColor(colorProvider.getColor(R.color.riotx_accent)) + iconRes(R.drawable.ic_camera) + iconColor(colorProvider.getColor(R.color.riotx_accent)) + listener { listener?.openCamera() } + } - bottomSheetVerificationActionItem { - id("openCamera") - title(stringProvider.getString(R.string.verification_scan_their_code)) - titleColor(colorProvider.getColor(R.color.riotx_accent)) - iconRes(R.drawable.ic_camera) - iconColor(colorProvider.getColor(R.color.riotx_accent)) - listener { listener?.openCamera() } - } - - dividerItem { - id("sep1") + dividerItem { + id("sep1") + } } bottomSheetVerificationActionItem { diff --git a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt index cd28a2b709..87d237d9dc 100644 --- a/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/crypto/verification/choose/VerificationChooseMethodViewModel.kt @@ -34,7 +34,8 @@ import im.vector.riotx.features.crypto.verification.VerificationBottomSheet data class VerificationChooseMethodViewState( val otherUserId: String = "", val transactionId: String = "", - val QRModeAvailable: Boolean = false, + val otherCanShowQrCode: Boolean = false, + val otherCanScanQrCode: Boolean = false, val QRtext: String? = null, val SASModeAvailable: Boolean = false ) : MvRxState @@ -50,15 +51,14 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( override fun verificationRequestUpdated(pr: PendingVerificationRequest) = withState { state -> val pvr = session.getSasVerificationService().getExistingVerificationRequest(state.otherUserId, state.transactionId) - val qrAvailable = pvr?.hasMethod(VerificationMethod.SCAN) ?: false - val emojiAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false setState { copy( - QRModeAvailable = qrAvailable, + otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false, + otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false, // TODO QRtext = "https://www.example.org", - SASModeAvailable = emojiAvailable + SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false ) } } @@ -87,15 +87,14 @@ class VerificationChooseMethodViewModel @AssistedInject constructor( val args: VerificationBottomSheet.VerificationArgs = viewModelContext.args() val session = (viewModelContext.activity as HasScreenInjector).injector().activeSessionHolder().getActiveSession() val pvr = session.getSasVerificationService().getExistingVerificationRequest(args.otherUserId, args.verificationId) - val qrAvailable = pvr?.hasMethod(VerificationMethod.SCAN) ?: false - val emojiAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false return VerificationChooseMethodViewState(otherUserId = args.otherUserId, transactionId = args.verificationId ?: "", - QRModeAvailable = qrAvailable, + otherCanShowQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SHOW) ?: false, + otherCanScanQrCode = pvr?.hasMethod(VerificationMethod.QR_CODE_SCAN) ?: false, // TODO QRtext = "https://www.example.org", - SASModeAvailable = emojiAvailable + SASModeAvailable = pvr?.hasMethod(VerificationMethod.SAS) ?: false ) } } From 4a1012cf816a2a5566c94ab147c8307a47cf2a65 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 23 Jan 2020 11:48:08 +0100 Subject: [PATCH 13/17] Add TODOs --- .../android/api/session/crypto/sas/SasVerificationService.kt | 2 ++ 1 file changed, 2 insertions(+) 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 a626dd5573..a5230fb995 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 @@ -27,6 +27,7 @@ import im.vector.matrix.android.internal.crypto.verification.PendingVerification * SAS verification is intended to be a highly interactive process for users, * and as such exposes verification methods which are easier for users to use. */ +// TODO Rename to VerificationService and reorganize packages? interface SasVerificationService { fun addListener(listener: SasVerificationListener) @@ -69,6 +70,7 @@ interface SasVerificationService { // fun transactionUpdated(tx: SasVerificationTransaction) + // TODO Rename to VerificationListener interface SasVerificationListener { fun transactionCreated(tx: SasVerificationTransaction) fun transactionUpdated(tx: SasVerificationTransaction) From e8a4f1fb90c147061c9fc0a61138a3d159134653 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 Jan 2020 16:04:03 +0100 Subject: [PATCH 14/17] Fix: .cancel won't appear in debug show all --- .../room/detail/timeline/factory/VerificationItemFactory.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/VerificationItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/VerificationItemFactory.kt index 5e24c69ad8..dc5bd740dd 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/VerificationItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/VerificationItemFactory.kt @@ -108,7 +108,7 @@ class VerificationItemFactory @Inject constructor( .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) } - else -> ignoredConclusion(event, highlight, callback) + else -> return ignoredConclusion(event, highlight, callback) } } EventType.KEY_VERIFICATION_DONE -> { From 65cb812fc6035f8793334baf48cdf6afb61a8fbf Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 Jan 2020 16:04:29 +0100 Subject: [PATCH 15/17] Fix / Unknown transaction when started by other after request --- .../verification/DefaultSasVerificationService.kt | 4 +++- .../crypto/verification/VerificationBottomSheet.kt | 4 +++- .../detail/timeline/factory/TimelineItemFactory.kt | 12 ++++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) 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 1b909c11aa..d8a98ff280 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 @@ -41,6 +41,7 @@ import im.vector.matrix.android.internal.session.SessionScope import im.vector.matrix.android.internal.util.MatrixCoroutineDispatchers import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch +import okhttp3.internal.toImmutableList import timber.log.Timber import java.util.* import javax.inject.Inject @@ -726,10 +727,11 @@ internal class DefaultSasVerificationService @Inject constructor( val transport = sasTransportRoomMessageFactory.createTransport(roomId, null) // Cancel existing pending requests? - requestsForUser.forEach { existingRequest -> + requestsForUser.toImmutableList().forEach { existingRequest -> existingRequest.transactionId?.let { tid -> if (!existingRequest.isFinished) { Timber.d("## SAS, cancelling pending requests to start a new one") + updatePendingRequest(existingRequest.copy(cancelConclusion = CancelCode.User)) transport.cancelTransaction(tid, existingRequest.otherUserId, "", CancelCode.User) } } 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 14c32efc27..bd7c280295 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 @@ -131,7 +131,9 @@ class VerificationBottomSheet : VectorBaseBottomSheetDialogFragment() { showFragment(VerificationEmojiCodeFragment::class, Bundle().apply { putParcelable(MvRx.KEY_ARG, VerificationArgs( it.otherUserMxItem?.id ?: "", - it.transactionId)) + // If it was outgoing it.transaction id would be null, but the pending request + // would be updated (from localID to txId) + it.pendingRequest?.transactionId ?: it.transactionId)) }) } SasVerificationTxState.Verified, diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt index 4704a3de85..acaf48ea56 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/timeline/factory/TimelineItemFactory.kt @@ -20,6 +20,7 @@ import im.vector.matrix.android.api.session.events.model.EventType import im.vector.matrix.android.api.session.room.timeline.TimelineEvent import im.vector.riotx.core.epoxy.EmptyItem_ import im.vector.riotx.core.epoxy.VectorEpoxyModel +import im.vector.riotx.core.resources.UserPreferencesProvider import im.vector.riotx.features.home.room.detail.timeline.TimelineEventController import timber.log.Timber import javax.inject.Inject @@ -29,7 +30,8 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me private val noticeItemFactory: NoticeItemFactory, private val defaultItemFactory: DefaultItemFactory, private val roomCreateItemFactory: RoomCreateItemFactory, - private val verificationConclusionItemFactory: VerificationItemFactory) { + private val verificationConclusionItemFactory: VerificationItemFactory, + private val userPreferencesProvider: UserPreferencesProvider ) { fun create(event: TimelineEvent, nextEvent: TimelineEvent?, @@ -73,9 +75,11 @@ class TimelineItemFactory @Inject constructor(private val messageItemFactory: Me EventType.KEY_VERIFICATION_KEY, EventType.KEY_VERIFICATION_READY, EventType.KEY_VERIFICATION_MAC -> { - // These events are filtered from timeline in normal case - // Only visible in developer mode - noticeItemFactory.create(event, highlight, callback) + // TODO These are not filtered out by timeline when encrypted + // For now manually ignore + if (userPreferencesProvider.shouldShowHiddenEvents()) { + noticeItemFactory.create(event, highlight, callback) + } else null } EventType.KEY_VERIFICATION_CANCEL, EventType.KEY_VERIFICATION_DONE -> { From 0148949a4fecf0cce8985e41a6dfe5d4c4e3afd2 Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 Jan 2020 16:13:46 +0100 Subject: [PATCH 16/17] Fix / prevent verification toaster to show when in good room --- .../java/im/vector/riotx/features/popup/PopupAlertManager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt b/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt index 6d92d36d38..a5e3f21f96 100644 --- a/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt +++ b/vector/src/main/java/im/vector/riotx/features/popup/PopupAlertManager.kt @@ -120,6 +120,8 @@ object PopupAlertManager { } currentAlerter = next next?.let { + + if (next.shouldBeDisplayedIn?.invoke(currentActivity) == false) return val currentTime = System.currentTimeMillis() if (next.expirationTimestamp != null && currentTime > next.expirationTimestamp!!) { // skip From 91ae96a153592d4a2e81447845bee6d01ea9a90d Mon Sep 17 00:00:00 2001 From: Valere Date: Thu, 23 Jan 2020 16:16:57 +0100 Subject: [PATCH 17/17] QuickFix / Do not verify yourself in dm --- .../riotx/features/home/room/detail/RoomDetailViewModel.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt index 0f97d19135..7069a66f5e 100644 --- a/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt +++ b/vector/src/main/java/im/vector/riotx/features/home/room/detail/RoomDetailViewModel.kt @@ -823,6 +823,7 @@ class RoomDetailViewModel @AssistedInject constructor(@Assisted initialState: Ro } private fun handleRequestVerification(action: RoomDetailAction.RequestVerification) { + if (action.userId == session.myUserId) return _requestLiveData.postValue(LiveEvent(Success(action))) }